From 5c05d6f230e34cf409529d87b71f768a384ae4b4 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Tue, 27 Jan 2026 08:26:00 +0000 Subject: [PATCH 001/215] 8374686: ZGC: Convert zMarkTerminate to use Atomic Reviewed-by: stefank, kbarrett --- src/hotspot/share/gc/z/zMarkTerminate.hpp | 9 ++-- .../share/gc/z/zMarkTerminate.inline.hpp | 45 +++++++++---------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/hotspot/share/gc/z/zMarkTerminate.hpp b/src/hotspot/share/gc/z/zMarkTerminate.hpp index cff1f8e73fa..9def3e72120 100644 --- a/src/hotspot/share/gc/z/zMarkTerminate.hpp +++ b/src/hotspot/share/gc/z/zMarkTerminate.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, 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 @@ -25,6 +25,7 @@ #define SHARE_GC_Z_ZMARKTERMINATE_HPP #include "gc/z/zLock.hpp" +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" class ZMarkStripeSet; @@ -32,9 +33,9 @@ class ZMarkStripeSet; class ZMarkTerminate { private: uint _nworkers; - volatile uint _nworking; - volatile uint _nawakening; - volatile bool _resurrected; + Atomic _nworking; + Atomic _nawakening; + Atomic _resurrected; ZConditionLock _lock; void maybe_reduce_stripes(ZMarkStripeSet* stripes, size_t used_nstripes); diff --git a/src/hotspot/share/gc/z/zMarkTerminate.inline.hpp b/src/hotspot/share/gc/z/zMarkTerminate.inline.hpp index 575044e3a39..84a545623bc 100644 --- a/src/hotspot/share/gc/z/zMarkTerminate.inline.hpp +++ b/src/hotspot/share/gc/z/zMarkTerminate.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 @@ -30,7 +30,6 @@ #include "gc/z/zLock.inline.hpp" #include "gc/z/zMarkStack.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/osThread.hpp" #include "runtime/thread.inline.hpp" @@ -42,24 +41,23 @@ inline ZMarkTerminate::ZMarkTerminate() _lock() {} inline void ZMarkTerminate::reset(uint nworkers) { - AtomicAccess::store(&_nworkers, nworkers); - AtomicAccess::store(&_nworking, nworkers); - _nawakening = 0; + _nworkers = nworkers; + _nworking.store_relaxed(nworkers); + _nawakening.store_relaxed(0u); } inline void ZMarkTerminate::leave() { SuspendibleThreadSetLeaver sts_leaver; ZLocker locker(&_lock); - AtomicAccess::store(&_nworking, _nworking - 1); - if (_nworking == 0) { + if (_nworking.sub_then_fetch(1u, memory_order_relaxed) == 0) { // Last thread leaving; notify waiters _lock.notify_all(); } } inline void ZMarkTerminate::maybe_reduce_stripes(ZMarkStripeSet* stripes, size_t used_nstripes) { - size_t nstripes = stripes->nstripes(); + const size_t nstripes = stripes->nstripes(); if (used_nstripes == nstripes && nstripes > 1u) { stripes->try_set_nstripes(nstripes, nstripes >> 1); } @@ -69,8 +67,7 @@ inline bool ZMarkTerminate::try_terminate(ZMarkStripeSet* stripes, size_t used_n SuspendibleThreadSetLeaver sts_leaver; ZLocker locker(&_lock); - AtomicAccess::store(&_nworking, _nworking - 1); - if (_nworking == 0) { + if (_nworking.sub_then_fetch(1u, memory_order_relaxed) == 0) { // Last thread entering termination: success _lock.notify_all(); return true; @@ -83,24 +80,24 @@ inline bool ZMarkTerminate::try_terminate(ZMarkStripeSet* stripes, size_t used_n // We either got notification about more work // or got a spurious wakeup; don't terminate - if (_nawakening > 0) { - AtomicAccess::store(&_nawakening, _nawakening - 1); + if (_nawakening.load_relaxed() > 0) { + _nawakening.sub_then_fetch(1u, memory_order_relaxed); } - if (_nworking == 0) { + if (_nworking.load_relaxed() == 0) { // We got notified all work is done; terminate return true; } - AtomicAccess::store(&_nworking, _nworking + 1); + _nworking.add_then_fetch(1u, memory_order_relaxed); return false; } inline void ZMarkTerminate::wake_up() { - uint nworking = AtomicAccess::load(&_nworking); - uint nawakening = AtomicAccess::load(&_nawakening); - if (nworking + nawakening == AtomicAccess::load(&_nworkers)) { + const uint nworking = _nworking.load_relaxed(); + const uint nawakening = _nawakening.load_relaxed(); + if (nworking + nawakening == _nworkers) { // Everyone is working or about to return; } @@ -111,24 +108,24 @@ inline void ZMarkTerminate::wake_up() { } ZLocker locker(&_lock); - if (_nworking + _nawakening != _nworkers) { + if (_nworking.load_relaxed() + _nawakening.load_relaxed() != _nworkers) { // Everyone is not working - AtomicAccess::store(&_nawakening, _nawakening + 1); + _nawakening.add_then_fetch(1u, memory_order_relaxed); _lock.notify(); } } inline bool ZMarkTerminate::saturated() const { - uint nworking = AtomicAccess::load(&_nworking); - uint nawakening = AtomicAccess::load(&_nawakening); + const uint nworking = _nworking.load_relaxed(); + const uint nawakening = _nawakening.load_relaxed(); - return nworking + nawakening == AtomicAccess::load(&_nworkers); + return nworking + nawakening == _nworkers; } inline void ZMarkTerminate::set_resurrected(bool value) { // Update resurrected if it changed if (resurrected() != value) { - AtomicAccess::store(&_resurrected, value); + _resurrected.store_relaxed(value); if (value) { log_debug(gc, marking)("Resurrection broke termination"); } else { @@ -138,7 +135,7 @@ inline void ZMarkTerminate::set_resurrected(bool value) { } inline bool ZMarkTerminate::resurrected() const { - return AtomicAccess::load(&_resurrected); + return _resurrected.load_relaxed(); } #endif // SHARE_GC_Z_ZMARKTERMINATE_INLINE_HPP From bd92c68ef0aa7615c62626eb6baf4496b0137cad Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Tue, 27 Jan 2026 08:36:41 +0000 Subject: [PATCH 002/215] 8374687: ZGC: Convert zNMethodTableIteration to use Atomic Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/z/zNMethodTableIteration.cpp | 9 ++++----- src/hotspot/share/gc/z/zNMethodTableIteration.hpp | 9 +++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/z/zNMethodTableIteration.cpp b/src/hotspot/share/gc/z/zNMethodTableIteration.cpp index bdd4270ddac..423e98dd28b 100644 --- a/src/hotspot/share/gc/z/zNMethodTableIteration.cpp +++ b/src/hotspot/share/gc/z/zNMethodTableIteration.cpp @@ -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,7 +24,6 @@ #include "gc/z/zNMethodTableEntry.hpp" #include "gc/z/zNMethodTableIteration.hpp" #include "memory/iterator.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -42,11 +41,11 @@ void ZNMethodTableIteration::nmethods_do_begin(ZNMethodTableEntry* table, size_t _table = table; _size = size; - _claimed = 0; + _claimed.store_relaxed(0u); } void ZNMethodTableIteration::nmethods_do_end() { - assert(_claimed >= _size, "Failed to claim all table entries"); + assert(_claimed.load_relaxed() >= _size, "Failed to claim all table entries"); // Finish iteration _table = nullptr; @@ -57,7 +56,7 @@ void ZNMethodTableIteration::nmethods_do(NMethodClosure* cl) { // Claim table partition. Each partition is currently sized to span // two cache lines. This number is just a guess, but seems to work well. const size_t partition_size = (ZCacheLineSize * 2) / sizeof(ZNMethodTableEntry); - const size_t partition_start = MIN2(AtomicAccess::fetch_then_add(&_claimed, partition_size), _size); + const size_t partition_start = MIN2(_claimed.fetch_then_add(partition_size), _size); const size_t partition_end = MIN2(partition_start + partition_size, _size); if (partition_start == partition_end) { // End of table diff --git a/src/hotspot/share/gc/z/zNMethodTableIteration.hpp b/src/hotspot/share/gc/z/zNMethodTableIteration.hpp index 064c3eafca0..12dc096491d 100644 --- a/src/hotspot/share/gc/z/zNMethodTableIteration.hpp +++ b/src/hotspot/share/gc/z/zNMethodTableIteration.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 @@ -25,15 +25,16 @@ #define SHARE_GC_Z_ZNMETHODTABLEITERATION_HPP #include "gc/z/zGlobals.hpp" +#include "runtime/atomic.hpp" class NMethodClosure; class ZNMethodTableEntry; class ZNMethodTableIteration { private: - ZNMethodTableEntry* _table; - size_t _size; - ZCACHE_ALIGNED volatile size_t _claimed; + ZNMethodTableEntry* _table; + size_t _size; + ZCACHE_ALIGNED Atomic _claimed; bool in_progress() const; From 6fda44172e955d4e1d181598a97902ed5b16c57b Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Tue, 27 Jan 2026 08:42:44 +0000 Subject: [PATCH 003/215] 8374690: ZGC: Convert zRelocate to use Atomic Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/z/zRelocate.cpp | 26 +++++++++++++------------- src/hotspot/share/gc/z/zRelocate.hpp | 7 ++++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index da07f67d859..d51cf5abbae 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.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 @@ -64,31 +64,31 @@ ZRelocateQueue::ZRelocateQueue() _needs_attention(0) {} bool ZRelocateQueue::needs_attention() const { - return AtomicAccess::load(&_needs_attention) != 0; + return _needs_attention.load_relaxed() != 0; } void ZRelocateQueue::inc_needs_attention() { - const int needs_attention = AtomicAccess::add(&_needs_attention, 1); + const int needs_attention = _needs_attention.add_then_fetch(1); assert(needs_attention == 1 || needs_attention == 2, "Invalid state"); } void ZRelocateQueue::dec_needs_attention() { - const int needs_attention = AtomicAccess::sub(&_needs_attention, 1); + const int needs_attention = _needs_attention.sub_then_fetch(1); assert(needs_attention == 0 || needs_attention == 1, "Invalid state"); } void ZRelocateQueue::activate(uint nworkers) { - _is_active = true; + _is_active.store_relaxed(true); join(nworkers); } void ZRelocateQueue::deactivate() { - AtomicAccess::store(&_is_active, false); + _is_active.store_relaxed(false); clear(); } bool ZRelocateQueue::is_active() const { - return AtomicAccess::load(&_is_active); + return _is_active.load_relaxed(); } void ZRelocateQueue::join(uint nworkers) { @@ -453,7 +453,7 @@ static void retire_target_page(ZGeneration* generation, ZPage* page) { class ZRelocateSmallAllocator { private: ZGeneration* const _generation; - volatile size_t _in_place_count; + Atomic _in_place_count; public: ZRelocateSmallAllocator(ZGeneration* generation) @@ -463,7 +463,7 @@ public: ZPage* alloc_and_retire_target_page(ZForwarding* forwarding, ZPage* target) { ZPage* const page = alloc_page(forwarding); if (page == nullptr) { - AtomicAccess::inc(&_in_place_count); + _in_place_count.add_then_fetch(1u); } if (target != nullptr) { @@ -493,7 +493,7 @@ public: } size_t in_place_count() const { - return _in_place_count; + return _in_place_count.load_relaxed(); } }; @@ -503,7 +503,7 @@ private: ZConditionLock _lock; ZRelocationTargets* _shared_targets; bool _in_place; - volatile size_t _in_place_count; + Atomic _in_place_count; public: ZRelocateMediumAllocator(ZGeneration* generation, ZRelocationTargets* shared_targets) @@ -539,7 +539,7 @@ public: ZPage* const to_page = alloc_page(forwarding); _shared_targets->set(partition_id, to_age, to_page); if (to_page == nullptr) { - AtomicAccess::inc(&_in_place_count); + _in_place_count.add_then_fetch(1u); _in_place = true; } @@ -579,7 +579,7 @@ public: } size_t in_place_count() const { - return _in_place_count; + return _in_place_count.load_relaxed(); } }; diff --git a/src/hotspot/share/gc/z/zRelocate.hpp b/src/hotspot/share/gc/z/zRelocate.hpp index 038efba83eb..d25536e1bce 100644 --- a/src/hotspot/share/gc/z/zRelocate.hpp +++ b/src/hotspot/share/gc/z/zRelocate.hpp @@ -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 @@ -28,6 +28,7 @@ #include "gc/z/zPageAge.hpp" #include "gc/z/zRelocationSet.hpp" #include "gc/z/zValue.hpp" +#include "runtime/atomic.hpp" class ZForwarding; class ZGeneration; @@ -42,8 +43,8 @@ private: uint _nworkers; uint _nsynchronized; bool _synchronize; - volatile bool _is_active; - volatile int _needs_attention; + Atomic _is_active; + Atomic _needs_attention; bool needs_attention() const; void inc_needs_attention(); From ee2deaded82e5fbd94aff7dd22cf2d5c57caa94e Mon Sep 17 00:00:00 2001 From: Varada M Date: Tue, 27 Jan 2026 10:01:02 +0000 Subject: [PATCH 004/215] 8371187: [BigEndian Platforms] Vector lane reversal error Reviewed-by: mdoerr, amitkumar --- .../jdk/incubator/vector/AbstractVector.java | 42 ++++++++++++++++++- .../jdk/incubator/vector/ByteVector.java | 8 ++++ .../jdk/incubator/vector/DoubleVector.java | 12 ++++++ .../jdk/incubator/vector/FloatVector.java | 12 ++++++ .../jdk/incubator/vector/IntVector.java | 12 ++++++ .../jdk/incubator/vector/LongVector.java | 12 ++++++ .../jdk/incubator/vector/ShortVector.java | 12 ++++++ 7 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java index 350931cd443..09bb0607759 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java @@ -182,7 +182,44 @@ abstract class AbstractVector extends Vector { final AbstractVector asVectorRawTemplate(LaneType laneType) { // NOTE: This assumes that convert0('X') // respects REGISTER_ENDIAN order. - return convert0('X', vspecies().withLanes(laneType)); + return convert0('X', vspecies().withLanes(laneType)).swapIfNeeded(vspecies()); + } + + @ForceInline + protected static VectorShuffle normalizeSubLanesForSpecies(AbstractSpecies targetSpecies, int subLanesPerSrc) { + final int lanes = targetSpecies.laneCount(); + + if ((lanes % subLanesPerSrc) != 0) { + throw new IllegalArgumentException("laneCount " + lanes + " not divisible by subLanesPerSrc " + subLanesPerSrc); + } + + // Each group corresponds to one source lane. + // For each group, reverse the lanes inside that group. + final int groups = lanes / subLanesPerSrc; + int[] map = new int[lanes]; + for (int g = 0; g < groups; ++g) { + int base = g * subLanesPerSrc; + for (int j = 0; j < subLanesPerSrc; ++j) { + map[base + j] = base + (subLanesPerSrc - 1 - j); + } + } + return VectorShuffle.fromArray(targetSpecies, map, 0); + } + + @ForceInline + protected final int subLanesToSwap(AbstractSpecies srcSpecies) { + if (java.nio.ByteOrder.nativeOrder() != ByteOrder.BIG_ENDIAN) { + return -1; + } + int sBytes = srcSpecies.elementSize(); + int tBytes = vspecies().elementSize(); + + // No lane reordering needed for same size or widening reinterprets + if (sBytes == tBytes || (sBytes % tBytes) != 0) { + return -1; + } + int subLanesPerSrc = sBytes / tBytes; + return subLanesPerSrc; } /*package-private*/ @@ -242,6 +279,9 @@ abstract class AbstractVector extends Vector { /*package-private*/ abstract AbstractVector maybeSwap(ByteOrder bo); + /*package-private*/ + abstract AbstractVector swapIfNeeded(AbstractSpecies srcSpecies); + /*package-private*/ @ForceInline VectorShuffle swapBytesShuffle() { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index 02e15d5f8dd..c071026a3b3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -4101,6 +4101,14 @@ public abstract class ByteVector extends AbstractVector { return this; } + /*package-private*/ + @Override + @ForceInline + final + ByteVector swapIfNeeded(AbstractSpecies srcSpecies) { + return this; + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_BYTE_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index 08fda9c96e6..e280ca4150c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -3612,6 +3612,18 @@ public abstract class DoubleVector extends AbstractVector { return this; } + @Override + @ForceInline + final + DoubleVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (DoubleVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_DOUBLE_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 0f70a2b81c8..35a8f4a78cf 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -3562,6 +3562,18 @@ public abstract class FloatVector extends AbstractVector { return this; } + @Override + @ForceInline + final + FloatVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (FloatVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_FLOAT_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index 23e703dcada..f340aed4fce 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -3720,6 +3720,18 @@ public abstract class IntVector extends AbstractVector { return this; } + @Override + @ForceInline + final + IntVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (IntVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_INT_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index 58bfd4d7772..b842bdebdc4 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -3655,6 +3655,18 @@ public abstract class LongVector extends AbstractVector { return this; } + @Override + @ForceInline + final + LongVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (LongVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_LONG_INDEX_SCALE); static final long ARRAY_BASE = diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java index 7ab7e7c4417..8b4c9bc5a77 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java @@ -4074,6 +4074,18 @@ public abstract class ShortVector extends AbstractVector { return this; } + @Override + @ForceInline + final + ShortVector swapIfNeeded(AbstractSpecies srcSpecies) { + int subLanesPerSrc = subLanesToSwap(srcSpecies); + if (subLanesPerSrc < 0) { + return this; + } + VectorShuffle shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc); + return (ShortVector) this.rearrange(shuffle); + } + static final int ARRAY_SHIFT = 31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_SHORT_INDEX_SCALE); static final long ARRAY_BASE = From e0445c09f7a967843a56634f72c7545446791e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Tue, 27 Jan 2026 10:25:58 +0000 Subject: [PATCH 005/215] 8376294: ZipFile.Source.Key should not hold on to its BasicFileAttributes instance Reviewed-by: jpai --- .../share/classes/java/util/zip/ZipFile.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index 7fa507980c2..bf43499e1f9 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -37,6 +37,7 @@ import java.nio.charset.Charset; import java.nio.file.InvalidPathException; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.Files; +import java.nio.file.attribute.FileTime; import java.util.*; import java.util.function.Consumer; import java.util.function.IntFunction; @@ -1444,11 +1445,13 @@ public class ZipFile implements ZipConstants, Closeable { * The unique combination of these components identifies a Source of a ZipFile. */ private static class Key { - private final BasicFileAttributes attrs; private final File file; + private final Object fileKey; + private final FileTime lastModifiedTime; // the Charset that was provided when constructing the ZipFile instance private final Charset charset; + /** * Constructs a {@code Key} to a {@code Source} of a {@code ZipFile} * @@ -1457,7 +1460,8 @@ public class ZipFile implements ZipConstants, Closeable { * @param charset the Charset that was provided when constructing the ZipFile instance */ public Key(File file, BasicFileAttributes attrs, Charset charset) { - this.attrs = attrs; + this.fileKey = attrs.fileKey(); + this.lastModifiedTime = attrs.lastModifiedTime(); this.file = file; this.charset = charset; } @@ -1465,10 +1469,9 @@ public class ZipFile implements ZipConstants, Closeable { @Override public int hashCode() { long t = charset.hashCode(); - t += attrs.lastModifiedTime().toMillis(); - Object fk = attrs.fileKey(); + t += lastModifiedTime.toMillis(); return Long.hashCode(t) + - (fk != null ? fk.hashCode() : file.hashCode()); + (fileKey != null ? fileKey.hashCode() : file.hashCode()); } @Override @@ -1477,12 +1480,12 @@ public class ZipFile implements ZipConstants, Closeable { if (!charset.equals(key.charset)) { return false; } - if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) { + if (!lastModifiedTime.equals(key.lastModifiedTime)) { return false; } - Object fk = attrs.fileKey(); - if (fk != null) { - return fk.equals(key.attrs.fileKey()); + + if (fileKey != null) { + return fileKey.equals(key.fileKey); } else { return file.equals(key.file); } From b1aea5520592e835e33762e349615fe616576103 Mon Sep 17 00:00:00 2001 From: Axel Boldt-Christmas Date: Tue, 27 Jan 2026 10:26:29 +0000 Subject: [PATCH 006/215] 8374695: ZGC: Convert zTLABUsage to use Atomic Reviewed-by: stefank, tschatzl --- src/hotspot/share/gc/z/zTLABUsage.cpp | 11 +++++------ src/hotspot/share/gc/z/zTLABUsage.hpp | 7 ++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/z/zTLABUsage.cpp b/src/hotspot/share/gc/z/zTLABUsage.cpp index c2e3db6bedf..d3613cf8632 100644 --- a/src/hotspot/share/gc/z/zTLABUsage.cpp +++ b/src/hotspot/share/gc/z/zTLABUsage.cpp @@ -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 @@ -23,24 +23,23 @@ #include "gc/z/zTLABUsage.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" ZTLABUsage::ZTLABUsage() : _used(0), _used_history() {} void ZTLABUsage::increase_used(size_t size) { - AtomicAccess::add(&_used, size, memory_order_relaxed); + _used.add_then_fetch(size, memory_order_relaxed); } void ZTLABUsage::decrease_used(size_t size) { - precond(size <= _used); + precond(size <= _used.load_relaxed()); - AtomicAccess::sub(&_used, size, memory_order_relaxed); + _used.sub_then_fetch(size, memory_order_relaxed); } void ZTLABUsage::reset() { - const size_t used = AtomicAccess::xchg(&_used, (size_t) 0); + const size_t used = _used.exchange(0u); // Avoid updates when nothing has been allocated since the last YC if (used == 0) { diff --git a/src/hotspot/share/gc/z/zTLABUsage.hpp b/src/hotspot/share/gc/z/zTLABUsage.hpp index 3d1b084fe16..68c7620c26e 100644 --- a/src/hotspot/share/gc/z/zTLABUsage.hpp +++ b/src/hotspot/share/gc/z/zTLABUsage.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 @@ -24,6 +24,7 @@ #ifndef SHARE_GC_Z_ZTLABUSAGE_HPP #define SHARE_GC_Z_ZTLABUSAGE_HPP +#include "runtime/atomic.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/numberSeq.hpp" @@ -42,9 +43,9 @@ class ZTLABUsage { private: // Accounting TLAB used until the next GC cycle - volatile size_t _used; + Atomic _used; // Sequence of historic used values - TruncatedSeq _used_history; + TruncatedSeq _used_history; public: ZTLABUsage(); From 4ff5f3a8c0910e9ed9d77586bd692c469bdf3460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Tue, 27 Jan 2026 10:28:54 +0000 Subject: [PATCH 007/215] 8376271: ZipFile comment confusingly refers to "native" ZIP file implementation Reviewed-by: jpai --- src/java.base/share/classes/java/util/zip/ZipFile.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index bf43499e1f9..a198c35c366 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -87,11 +87,11 @@ public class ZipFile implements ZipConstants, Closeable { private final ZipCoder zipCoder; private volatile boolean closeRequested; - // The "resource" used by this ZIP file that needs to be - // cleaned after use. + // An object holding state which needs to be cleaned after + // this ZipFile is closed or becomes unreachable: // a) the input streams that need to be closed // b) the list of cached Inflater objects - // c) the "native" source of this ZIP file. + // c) the Source object providing read access to the actual ZIP file private final @Stable CleanableResource res; private static final int STORED = ZipEntry.STORED; From 5990165d8257f39595b4c38f4e3e8d6ebb3393e8 Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Tue, 27 Jan 2026 11:55:25 +0000 Subject: [PATCH 008/215] 8358957: [ubsan]: The assert in layout_helper_boolean_diffbit() in klass.hpp needs UB to fail Reviewed-by: dlong, jsjolen --- src/hotspot/share/oops/klass.hpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index b6974762209..d59db9744cb 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -510,18 +510,20 @@ protected: return (BasicType) btvalue; } - // Want a pattern to quickly diff against layout header in register - // find something less clever! + // Return a value containing a single set bit that is in the bitset difference between the + // layout helpers for array-of-boolean and array-of-byte. static int layout_helper_boolean_diffbit() { - jint zlh = array_layout_helper(T_BOOLEAN); - jint blh = array_layout_helper(T_BYTE); - assert(zlh != blh, "array layout helpers must differ"); - int diffbit = 1; - while ((diffbit & (zlh ^ blh)) == 0 && (diffbit & zlh) == 0) { - diffbit <<= 1; - assert(diffbit != 0, "make sure T_BOOLEAN has a different bit than T_BYTE"); - } - return diffbit; + uint zlh = static_cast(array_layout_helper(T_BOOLEAN)); + uint blh = static_cast(array_layout_helper(T_BYTE)); + // get all the bits that are set in zlh and clear in blh + uint candidates = (zlh & ~blh); + assert(candidates != 0, "must be"); // must be some if there is a solution. + // Use well known bit hack to isolate the low bit of candidates. + uint result = candidates & (-candidates); + assert(is_power_of_2(result), "must be power of 2"); + assert((result & zlh) != 0, "must be set in alh of T_BOOLEAN"); + assert((result & blh) == 0, "must be clear in alh of T_BYTE"); + return static_cast(result); } static int layout_helper_log2_element_size(jint lh) { From 528bbe7919785c50dda583277f4146b25eb4d2a4 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Tue, 27 Jan 2026 12:33:43 +0000 Subject: [PATCH 009/215] 8376302: os::Machine::used_memory reports container used memory when running containerized Reviewed-by: eosterlund, sgehwolf --- src/hotspot/share/runtime/os.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 1c06bf3c521..57971897400 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -2211,8 +2211,8 @@ bool os::used_memory(physical_memory_size_type& value) { bool os::Machine::used_memory(physical_memory_size_type& value) { physical_memory_size_type avail_mem = 0; // Return value ignored - defaulting to 0 on failure. - (void)os::available_memory(avail_mem); - physical_memory_size_type phys_mem = os::physical_memory(); + (void)os::Machine::available_memory(avail_mem); + physical_memory_size_type phys_mem = os::Machine::physical_memory(); value = phys_mem - avail_mem; return true; } From 40d1b642a43fbc5c6ad21417f2f9d62d99db0201 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 27 Jan 2026 12:51:20 +0000 Subject: [PATCH 010/215] 8376191: Remove AtomicAccess include from files that do not use it in gc/shared Reviewed-by: iwalulya, stefank --- src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp | 1 - src/hotspot/share/gc/shared/partialArrayState.cpp | 1 - src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp | 1 - src/hotspot/share/gc/shared/taskqueue.cpp | 1 - src/hotspot/share/gc/shared/taskqueue.inline.hpp | 1 - src/hotspot/share/gc/shared/workerThread.cpp | 1 - 6 files changed, 6 deletions(-) diff --git a/src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp b/src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp index 8e220e745e5..8341a3b20c4 100644 --- a/src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp +++ b/src/hotspot/share/gc/shared/oopStorageSetParState.inline.hpp @@ -31,7 +31,6 @@ #include "gc/shared/oopStorageSet.hpp" #include "memory/iterator.hpp" #include "oops/access.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/debug.hpp" template diff --git a/src/hotspot/share/gc/shared/partialArrayState.cpp b/src/hotspot/share/gc/shared/partialArrayState.cpp index 6f714d48a35..aadbc46b7c1 100644 --- a/src/hotspot/share/gc/shared/partialArrayState.cpp +++ b/src/hotspot/share/gc/shared/partialArrayState.cpp @@ -28,7 +28,6 @@ #include "memory/arena.hpp" #include "nmt/memTag.hpp" #include "oops/oopsHierarchy.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/orderAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" diff --git a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp index aaa86e2de16..6946f7c69ff 100644 --- a/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp +++ b/src/hotspot/share/gc/shared/partialArrayTaskStepper.inline.hpp @@ -28,7 +28,6 @@ #include "gc/shared/partialArrayTaskStepper.hpp" #include "gc/shared/partialArrayState.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/checkedCast.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/shared/taskqueue.cpp b/src/hotspot/share/gc/shared/taskqueue.cpp index f75dc4c2923..58af1793a48 100644 --- a/src/hotspot/share/gc/shared/taskqueue.cpp +++ b/src/hotspot/share/gc/shared/taskqueue.cpp @@ -25,7 +25,6 @@ #include "gc/shared/taskqueue.hpp" #include "logging/log.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" diff --git a/src/hotspot/share/gc/shared/taskqueue.inline.hpp b/src/hotspot/share/gc/shared/taskqueue.inline.hpp index 55851495a5f..e77645f4fcf 100644 --- a/src/hotspot/share/gc/shared/taskqueue.inline.hpp +++ b/src/hotspot/share/gc/shared/taskqueue.inline.hpp @@ -32,7 +32,6 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/orderAccess.hpp" #include "utilities/debug.hpp" #include "utilities/ostream.hpp" diff --git a/src/hotspot/share/gc/shared/workerThread.cpp b/src/hotspot/share/gc/shared/workerThread.cpp index e4831d25d26..2f6f003608f 100644 --- a/src/hotspot/share/gc/shared/workerThread.cpp +++ b/src/hotspot/share/gc/shared/workerThread.cpp @@ -26,7 +26,6 @@ #include "gc/shared/workerThread.hpp" #include "logging/log.hpp" #include "memory/iterator.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/init.hpp" #include "runtime/java.hpp" #include "runtime/os.hpp" From 992a8ef46bc0a06c70fd5f4f307dbd20e402ed33 Mon Sep 17 00:00:00 2001 From: Daniel Gredler Date: Tue, 27 Jan 2026 13:20:26 +0000 Subject: [PATCH 011/215] 8376226: CharsetEncoder.canEncode(CharSequence) is much slower than necessary Reviewed-by: alanb, naoto --- .../nio/charset/Charset-X-Coder.java.template | 18 +- .../share/classes/sun/nio/cs/DoubleByte.java | 12 +- .../share/classes/sun/nio/cs/ISO_8859_1.java | 12 +- .../share/classes/sun/nio/cs/SingleByte.java | 12 +- .../share/classes/sun/nio/cs/US_ASCII.java | 12 +- .../bench/java/nio/CharsetCanEncode.java | 187 ++++++++++++++++++ 6 files changed, 245 insertions(+), 8 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java diff --git a/src/java.base/share/classes/java/nio/charset/Charset-X-Coder.java.template b/src/java.base/share/classes/java/nio/charset/Charset-X-Coder.java.template index e900c2eca0f..aca987ed678 100644 --- a/src/java.base/share/classes/java/nio/charset/Charset-X-Coder.java.template +++ b/src/java.base/share/classes/java/nio/charset/Charset-X-Coder.java.template @@ -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 @@ -819,6 +819,12 @@ public abstract class Charset$Coder$ { */ public final $Otype$Buffer $code$($Itype$Buffer in) throws CharacterCodingException + { + return $code$(in, true); + } + + private $Otype$Buffer $code$($Itype$Buffer in, boolean throwOnError) + throws CharacterCodingException { int n = Math.min((int)(in.remaining() * average$ItypesPerOtype$()), ArraysSupport.SOFT_MAX_ARRAY_LENGTH); @@ -844,7 +850,11 @@ public abstract class Charset$Coder$ { out = o; continue; } - cr.throwException(); + if (throwOnError) { + cr.throwException(); + } else { + return null; + } } out.flip(); return out; @@ -938,7 +948,8 @@ public abstract class Charset$Coder$ { try { onMalformedInput(CodingErrorAction.REPORT); onUnmappableCharacter(CodingErrorAction.REPORT); - encode(cb); + ByteBuffer bb = encode(cb, false); + return bb != null; } catch (CharacterCodingException x) { return false; } finally { @@ -946,7 +957,6 @@ public abstract class Charset$Coder$ { onUnmappableCharacter(ua); reset(); } - return true; } /** diff --git a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 2a4dbdc95ed..0aaae14bbf9 100644 --- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.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. * 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,16 @@ public class DoubleByte { return encodeChar(c) != UNMAPPABLE_ENCODING; } + public boolean canEncode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length; i++) { + if (!canEncode(cs.charAt(i))) { + return false; + } + } + return true; + } + protected Surrogate.Parser sgp() { if (sgp == null) sgp = new Surrogate.Parser(); diff --git a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java index 39215bfa93d..9240ac3f380 100644 --- a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java +++ b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.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 @@ -136,6 +136,16 @@ public class ISO_8859_1 return c <= '\u00FF'; } + public boolean canEncode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length; i++) { + if (!canEncode(cs.charAt(i))) { + return false; + } + } + return true; + } + public boolean isLegalReplacement(byte[] repl) { return true; // we accept any byte value } diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index 59887b944d3..d4127b7c043 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.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 @@ -201,6 +201,16 @@ public class SingleByte return encode(c) != UNMAPPABLE_ENCODING; } + public boolean canEncode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length; i++) { + if (!canEncode(cs.charAt(i))) { + return false; + } + } + return true; + } + public boolean isLegalReplacement(byte[] repl) { return ((repl.length == 1 && repl[0] == (byte)'?') || super.isLegalReplacement(repl)); diff --git a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index bb84ab1bd4b..61c4948e949 100644 --- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.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 @@ -139,6 +139,16 @@ public class US_ASCII return c < 0x80; } + public boolean canEncode(CharSequence cs) { + int length = cs.length(); + for (int i = 0; i < length; i++) { + if (!canEncode(cs.charAt(i))) { + return false; + } + } + return true; + } + public boolean isLegalReplacement(byte[] repl) { return (repl.length == 1 && repl[0] >= 0) || super.isLegalReplacement(repl); diff --git a/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java b/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java new file mode 100644 index 00000000000..ebfbc217a95 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/nio/CharsetCanEncode.java @@ -0,0 +1,187 @@ +/* + * 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 org.openjdk.bench.java.nio; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Fork(3) +public class CharsetCanEncode { + + private static final char ALEF_CHAR = '\u05d0'; + private static final String ALEF_STRING = "\u05d0"; + + // sun.nio.cs.US_ASCII + private CharsetEncoder ascii = Charset.forName("US-ASCII").newEncoder(); + + // sun.nio.cs.ISO_8859_1 + private CharsetEncoder iso88591 = Charset.forName("ISO-8859-1").newEncoder(); + + // sun.nio.cs.SingleByte + private CharsetEncoder iso88592 = Charset.forName("ISO-8859-2").newEncoder(); + + // sun.nio.cs.DoubleByte + private CharsetEncoder shiftjis = Charset.forName("Shift_JIS").newEncoder(); + + // sun.nio.cs.UTF_8 + private CharsetEncoder utf8 = Charset.forName("UTF-8").newEncoder(); + + // sun.nio.cs.UTF_16LE + private CharsetEncoder utf16le = Charset.forName("UTF-16LE").newEncoder(); + + @Benchmark + public boolean asciiCanEncodeCharYes() { + return ascii.canEncode('D'); + } + + @Benchmark + public boolean asciiCanEncodeStringYes() { + return ascii.canEncode("D"); + } + + @Benchmark + public boolean asciiCanEncodeCharNo() { + return ascii.canEncode(ALEF_CHAR); + } + + @Benchmark + public boolean asciiCanEncodeStringNo() { + return ascii.canEncode(ALEF_STRING); + } + + @Benchmark + public boolean iso88591CanEncodeCharYes() { + return iso88591.canEncode('D'); + } + + @Benchmark + public boolean iso88591CanEncodeStringYes() { + return iso88591.canEncode("D"); + } + + @Benchmark + public boolean iso88591CanEncodeCharNo() { + return iso88591.canEncode(ALEF_CHAR); + } + + @Benchmark + public boolean iso88591CanEncodeStringNo() { + return iso88591.canEncode(ALEF_STRING); + } + + @Benchmark + public boolean iso88592CanEncodeCharYes() { + return iso88592.canEncode('D'); + } + + @Benchmark + public boolean iso88592CanEncodeStringYes() { + return iso88592.canEncode("D"); + } + + @Benchmark + public boolean iso88592CanEncodeCharNo() { + return iso88592.canEncode(ALEF_CHAR); + } + + @Benchmark + public boolean iso88592CanEncodeStringNo() { + return iso88592.canEncode(ALEF_STRING); + } + + @Benchmark + public boolean shiftjisCanEncodeCharYes() { + return shiftjis.canEncode('D'); + } + + @Benchmark + public boolean shiftjisCanEncodeStringYes() { + return shiftjis.canEncode("D"); + } + + @Benchmark + public boolean shiftjisCanEncodeCharNo() { + return shiftjis.canEncode(ALEF_CHAR); + } + + @Benchmark + public boolean shiftjisCanEncodeStringNo() { + return shiftjis.canEncode(ALEF_STRING); + } + + @Benchmark + public boolean utf8CanEncodeCharYes() { + return utf8.canEncode('D'); + } + + @Benchmark + public boolean utf8CanEncodeStringYes() { + return utf8.canEncode("D"); + } + + @Benchmark + public boolean utf8CanEncodeCharNo() { + return utf8.canEncode(Character.MIN_SURROGATE); + } + + @Benchmark + public boolean utf8CanEncodeStringNo() { + return utf8.canEncode(String.valueOf(Character.MIN_SURROGATE)); + } + + @Benchmark + public boolean utf16leCanEncodeCharYes() { + return utf16le.canEncode('D'); + } + + @Benchmark + public boolean utf16leCanEncodeStringYes() { + return utf16le.canEncode("D"); + } + + @Benchmark + public boolean utf16leCanEncodeCharNo() { + return utf16le.canEncode(Character.MIN_SURROGATE); + } + + @Benchmark + public boolean utf16leCanEncodeStringNo() { + return utf16le.canEncode(String.valueOf(Character.MIN_SURROGATE)); + } +} From 479ac8b2fdfbb64d26b34ff72abd61a1ce5f6c87 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 27 Jan 2026 13:30:14 +0000 Subject: [PATCH 012/215] 8376281: Remove USE_XLC_BUILTINS macro usage in AIX code Reviewed-by: mdoerr, clanger --- src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp | 8 ++------ src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp | 10 +--------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp index afef21b091a..3ab81697280 100644 --- a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp +++ b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2026 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 @@ -412,12 +412,8 @@ run_stub: } void os::Aix::init_thread_fpu_state(void) { -#if !defined(USE_XLC_BUILTINS) // Disable FP exceptions. __asm__ __volatile__ ("mtfsfi 6,0"); -#else - __mtfsfi(6, 0); -#endif } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp index c741335b5f0..d9dac0e231f 100644 --- a/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp +++ b/src/hotspot/os_cpu/aix_ppc/prefetch_aix_ppc.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2013 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 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 @@ -29,29 +29,21 @@ // Included in runtime/prefetch.inline.hpp inline void Prefetch::read(const void *loc, intx interval) { -#if !defined(USE_XLC_BUILTINS) __asm__ __volatile__ ( " dcbt 0, %0 \n" : : /*%0*/"r" ( ((address)loc) +((long)interval) ) //: ); -#else - __dcbt(((address)loc) +((long)interval)); -#endif } inline void Prefetch::write(void *loc, intx interval) { -#if !defined(USE_XLC_BUILTINS) __asm__ __volatile__ ( " dcbtst 0, %0 \n" : : /*%0*/"r" ( ((address)loc) +((long)interval) ) //: ); -#else - __dcbtst( ((address)loc) +((long)interval) ); -#endif } #endif // OS_CPU_AIX_PPC_PREFETCH_AIX_PPC_INLINE_HPP From 64b0ae6be8a7b70ed4cc08333447e9b73bdcbaca Mon Sep 17 00:00:00 2001 From: Wang Haomin Date: Tue, 27 Jan 2026 14:21:44 +0000 Subject: [PATCH 013/215] 8376276: Add javafx to allowed-list of CheckFiles Reviewed-by: erikj, kcr --- test/jdk/build/CheckFiles.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/jdk/build/CheckFiles.java b/test/jdk/build/CheckFiles.java index 5a915e881f0..7ef5b803d2d 100644 --- a/test/jdk/build/CheckFiles.java +++ b/test/jdk/build/CheckFiles.java @@ -106,6 +106,8 @@ public class CheckFiles { allowedEndingsLibDir.add("psfont.properties.ja"); allowedEndingsLibDir.add("src.zip"); allowedEndingsLibDir.add("tzdb.dat"); + allowedEndingsLibDir.add("javafx-swt.jar"); + allowedEndingsLibDir.add("javafx.properties"); if (Platform.isWindows()) { allowedEndingsLibDir.add(".lib"); allowedEndingsLibDir.add("tzmappings"); From bbb4b0d498900f929225233008bbdbafaae5d709 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 27 Jan 2026 14:51:04 +0000 Subject: [PATCH 014/215] 8376277: Migrate java/lang/reflect tests away from TestNG Reviewed-by: alanb --- .../AccessibleObject/CanAccessTest.java | 41 ++--- .../ModuleSetAccessibleTest.java | 75 +++------ .../TrySetAccessibleTest.java | 79 ++++----- .../java/lang/reflect/ChainedReflection.java | 42 +++-- .../FilterNotMostSpecific.java | 22 +-- .../DefaultStaticInvokeTest.java | 70 ++++---- .../DefaultStaticTestData.java | 16 +- .../java/lang/reflect/Field/NegativeTest.java | 159 +++++------------- .../lang/reflect/Generics/ThreadSafety.java | 9 +- .../lang/reflect/IllegalArgumentsTest.java | 8 +- .../lang/reflect/Method/MethodArityLimit.java | 18 +- .../reflect/MethodHandleAccessorsTest.java | 70 ++++---- .../lang/reflect/Proxy/DefaultMethods.java | 127 ++++++-------- .../reflect/Proxy/HiddenProxyInterface.java | 15 +- .../reflect/Proxy/LazyInitializationTest.java | 13 +- .../reflect/Proxy/ProxyClassAccessTest.java | 30 ++-- .../lang/reflect/Proxy/ProxyLayerTest.java | 32 ++-- .../java/lang/reflect/Proxy/ProxyTest.java | 20 +-- .../reflect/Proxy/SealedInterfaceTest.java | 15 +- .../java/lang/reflect/Proxy/TestVarArgs.java | 45 +++-- .../nonPublicProxy/DefaultMethodProxy.java | 40 ++--- .../annotationSharing/AnnotationSharing.java | 6 +- .../reflect/callerCache/CustomLoaderTest.java | 36 ++-- .../ReflectionCallerCacheTest.java | 23 +-- .../records/CheckEqualityIsBasedOnFields.java | 22 ++- .../lang/reflect/records/IsRecordTest.java | 44 ++--- .../reflect/records/RecordReflectionTest.java | 81 +++++---- .../SealedClassesReflectionTest.java | 59 ++++--- 28 files changed, 511 insertions(+), 706 deletions(-) diff --git a/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java b/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java index 45e2502b601..ac7a1bad31b 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/CanAccessTest.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,11 +21,11 @@ * questions. */ -/** +/* * @test * @build CanAccessTest * @modules java.base/jdk.internal.misc:+open - * @run testng/othervm CanAccessTest + * @run junit/othervm CanAccessTest * @summary Test AccessibleObject::canAccess method */ @@ -34,31 +34,29 @@ import java.lang.reflect.Method; import java.security.SecureClassLoader; import jdk.internal.misc.Unsafe; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; -@Test public class CanAccessTest { private static Unsafe INSTANCE = Unsafe.getUnsafe(); /** * null object parameter for Constructor */ + @Test public void testConstructor() throws Exception { Constructor ctor = Unsafe.class.getDeclaredConstructor(); assertFalse(ctor.canAccess(null)); assertTrue(ctor.trySetAccessible()); - try { - // non-null object parameter - ctor.canAccess(INSTANCE); - assertTrue(false); - } catch (IllegalArgumentException expected) {} + // non-null object parameter + assertThrows(IllegalArgumentException.class, () -> ctor.canAccess(INSTANCE)); } /** * Test protected constructors */ + @Test public void testProtectedConstructor() throws Exception { TestLoader.testProtectedConstructorNonOpenedPackage(); @@ -69,21 +67,20 @@ public class CanAccessTest { /** * null object parameter for static members */ + @Test public void testStaticMember() throws Exception { Method m = Unsafe.class.getDeclaredMethod("throwIllegalAccessError"); assertFalse(m.canAccess(null)); assertTrue(m.trySetAccessible()); - try { - // non-null object parameter - m.canAccess(INSTANCE); - assertTrue(false); - } catch (IllegalArgumentException expected) { } + // non-null object parameter + assertThrows(IllegalArgumentException.class, () -> m.canAccess(INSTANCE)); } /** * Test protected static */ + @Test public void testProtectedStatic() throws Exception { Method m = TestLoader.testProtectedStatic(); assertFalse(m.canAccess(null)); @@ -93,28 +90,24 @@ public class CanAccessTest { * the specified object must be an instance of the declaring class * for instance members */ + @Test public void testInstanceMethod() throws Exception { Method m = Unsafe.class.getDeclaredMethod("allocateMemory0", long.class); assertFalse(m.canAccess(INSTANCE)); - try { - m.canAccess(null); - assertTrue(false); - } catch (IllegalArgumentException expected) { } + assertThrows(IllegalArgumentException.class, () -> m.canAccess(null)); } /** * the specified object must be an instance of the declaring class * for instance members */ + @Test public void testInvalidInstanceObject() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Method m = clazz.getDeclaredMethod("size"); - try { - m.canAccess(INSTANCE); - assertTrue(false); - } catch (IllegalArgumentException expected) { } + assertThrows(IllegalArgumentException.class, () -> m.canAccess(INSTANCE)); } diff --git a/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java b/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java index 45cebdb552b..7d79a1ac49b 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/ModuleSetAccessibleTest.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 @@ -21,12 +21,12 @@ * questions. */ -/** +/* * @test * @build ModuleSetAccessibleTest * @modules java.base/java.lang:open * java.base/jdk.internal.misc:+open - * @run testng/othervm ModuleSetAccessibleTest + * @run junit/othervm ModuleSetAccessibleTest * @summary Test java.lang.reflect.AccessibleObject with modules */ @@ -40,22 +40,19 @@ import java.security.ProtectionDomain; import jdk.internal.misc.Unsafe; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; -@Test public class ModuleSetAccessibleTest { /** * Invoke a private constructor on a public class in an exported package */ + @Test public void testPrivateConstructorInExportedPackage() throws Exception { Constructor ctor = Unsafe.class.getDeclaredConstructor(); - try { - ctor.newInstance(); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance()); ctor.setAccessible(true); Unsafe unsafe = (Unsafe) ctor.newInstance(); @@ -65,34 +62,26 @@ public class ModuleSetAccessibleTest { /** * Invoke a private method on a public class in an exported package */ + @Test public void testPrivateMethodInExportedPackage() throws Exception { Method m = Unsafe.class.getDeclaredMethod("throwIllegalAccessError"); - try { - m.invoke(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> m.invoke(null)); m.setAccessible(true); - try { - m.invoke(null); - assertTrue(false); - } catch (InvocationTargetException e) { - // thrown by throwIllegalAccessError - assertTrue(e.getCause() instanceof IllegalAccessError); - } + InvocationTargetException e = assertThrows(InvocationTargetException.class, () -> + m.invoke(null)); + // thrown by throwIllegalAccessError + assertInstanceOf(IllegalAccessError.class, e.getCause()); } /** * Access a private field in a public class that is an exported package */ + @Test public void testPrivateFieldInExportedPackage() throws Exception { Field f = Unsafe.class.getDeclaredField("theUnsafe"); - - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); f.setAccessible(true); Unsafe unsafe = (Unsafe) f.get(null); @@ -102,19 +91,14 @@ public class ModuleSetAccessibleTest { /** * Invoke a public constructor on a public class in a non-exported package */ + @Test public void testPublicConstructorInNonExportedPackage() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Constructor ctor = clazz.getConstructor(String.class); - try { - ctor.newInstance("cn=duke"); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance("cn=duke")); - try { - ctor.setAccessible(true); - assertTrue(false); - } catch (InaccessibleObjectException expected) { } + assertThrows(InaccessibleObjectException.class, () -> ctor.setAccessible(true)); ctor.setAccessible(false); // should succeed } @@ -123,19 +107,14 @@ public class ModuleSetAccessibleTest { /** * Access a public field in a public class that in a non-exported package */ + @Test public void testPublicFieldInNonExportedPackage() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Field f = clazz.getField("SERIALNUMBER_OID"); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); - try { - f.setAccessible(true); - assertTrue(false); - } catch (InaccessibleObjectException expected) { } + assertThrows(InaccessibleObjectException.class, () -> f.setAccessible(true)); f.setAccessible(false); // should succeed } @@ -144,6 +123,7 @@ public class ModuleSetAccessibleTest { /** * Test that the Class constructor cannot be make accessible. */ + @Test public void testJavaLangClass() throws Exception { // non-public constructor @@ -152,15 +132,8 @@ public class ModuleSetAccessibleTest { ProtectionDomain.class, boolean.class, char.class); AccessibleObject[] ctors = { ctor }; - try { - ctor.setAccessible(true); - assertTrue(false); - } catch (SecurityException expected) { } - - try { - AccessibleObject.setAccessible(ctors, true); - assertTrue(false); - } catch (SecurityException expected) { } + assertThrows(SecurityException.class, () -> ctor.setAccessible(true)); + assertThrows(SecurityException.class, () -> AccessibleObject.setAccessible(ctors, true)); // should succeed ctor.setAccessible(false); diff --git a/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java b/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java index 774ee4d1dad..bd4d0fab23f 100644 --- a/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.java +++ b/test/jdk/java/lang/reflect/AccessibleObject/TrySetAccessibleTest.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 @@ -21,14 +21,14 @@ * questions. */ -/** +/* * @test * @build TrySetAccessibleTest * @modules java.base/java.lang:open * java.base/jdk.internal.module * java.base/jdk.internal.perf * java.base/jdk.internal.misc:+open - * @run testng/othervm TrySetAccessibleTest + * @run junit/othervm TrySetAccessibleTest * @summary Test AccessibleObject::trySetAccessible method */ @@ -43,21 +43,18 @@ import jdk.internal.module.ModulePath; import jdk.internal.perf.Perf; import java.security.ProtectionDomain; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; -@Test public class TrySetAccessibleTest { /** * Invoke a private constructor on a public class in an exported package */ + @Test public void testPrivateConstructorInExportedPackage() throws Exception { Constructor ctor = Perf.class.getDeclaredConstructor(); - try { - ctor.newInstance(); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance()); assertFalse(ctor.trySetAccessible()); assertFalse(ctor.canAccess(null)); @@ -66,13 +63,11 @@ public class TrySetAccessibleTest { /** * Invoke a private constructor on a public class in an open package */ + @Test public void testPrivateConstructorInOpenedPackage() throws Exception { Constructor ctor = Unsafe.class.getDeclaredConstructor(); - try { - ctor.newInstance(); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance()); assertTrue(ctor.trySetAccessible()); assertTrue(ctor.canAccess(null)); @@ -82,12 +77,10 @@ public class TrySetAccessibleTest { /** * Invoke a private method on a public class in an exported package */ + @Test public void testPrivateMethodInExportedPackage() throws Exception { Method m = ModulePath.class.getDeclaredMethod("packageName", String.class); - try { - m.invoke(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> m.invoke(null)); assertFalse(m.trySetAccessible()); assertFalse(m.canAccess(null)); @@ -97,54 +90,42 @@ public class TrySetAccessibleTest { /** * Invoke a private method on a public class in an open package */ + @Test public void testPrivateMethodInOpenedPackage() throws Exception { Method m = Unsafe.class.getDeclaredMethod("throwIllegalAccessError"); assertFalse(m.canAccess(null)); - try { - m.invoke(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> m.invoke(null)); assertTrue(m.trySetAccessible()); assertTrue(m.canAccess(null)); - try { - m.invoke(null); - assertTrue(false); - } catch (InvocationTargetException e) { - // thrown by throwIllegalAccessError - assertTrue(e.getCause() instanceof IllegalAccessError); - } + + InvocationTargetException e = assertThrows(InvocationTargetException.class, () -> + m.invoke(null)); + assertInstanceOf(IllegalAccessError.class, e.getCause()); } /** * Invoke a private method on a public class in an exported package */ + @Test public void testPrivateFieldInExportedPackage() throws Exception { Field f = Perf.class.getDeclaredField("instance"); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); assertFalse(f.trySetAccessible()); assertFalse(f.canAccess(null)); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) {} + assertThrows(IllegalAccessException.class, () -> f.get(null)); } /** * Access a private field in a public class that is an exported package */ + @Test public void testPrivateFieldInOpenedPackage() throws Exception { Field f = Unsafe.class.getDeclaredField("theUnsafe"); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); assertTrue(f.trySetAccessible()); assertTrue(f.canAccess(null)); @@ -155,32 +136,29 @@ public class TrySetAccessibleTest { /** * Invoke a public constructor on a public class in a non-exported package */ + @Test public void testPublicConstructorInNonExportedPackage() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Constructor ctor = clazz.getConstructor(String.class); - try { - ctor.newInstance("cn=duke"); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> ctor.newInstance("cn=duke")); assertFalse(ctor.trySetAccessible()); assertFalse(ctor.canAccess(null)); - assertTrue(ctor.trySetAccessible() == ctor.isAccessible()); + assertFalse(ctor.trySetAccessible()); + assertFalse(ctor.isAccessible()); // should match trySetAccessible } /** * Access a public field in a public class that in a non-exported package */ + @Test public void testPublicFieldInNonExportedPackage() throws Exception { Class clazz = Class.forName("sun.security.x509.X500Name"); Field f = clazz.getField("SERIALNUMBER_OID"); - try { - f.get(null); - assertTrue(false); - } catch (IllegalAccessException expected) { } + assertThrows(IllegalAccessException.class, () -> f.get(null)); assertFalse(f.trySetAccessible()); assertFalse(f.canAccess(null)); @@ -190,6 +168,7 @@ public class TrySetAccessibleTest { /** * Test that the Class constructor cannot be make accessible. */ + @Test public void testJavaLangClass() throws Exception { // non-public constructor diff --git a/test/jdk/java/lang/reflect/ChainedReflection.java b/test/jdk/java/lang/reflect/ChainedReflection.java index 212a70345cd..bd76cd13fc8 100644 --- a/test/jdk/java/lang/reflect/ChainedReflection.java +++ b/test/jdk/java/lang/reflect/ChainedReflection.java @@ -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 @@ -23,9 +23,11 @@ /* * @test - * @run testng/othervm ChainedReflection + * @run junit/othervm ChainedReflection * @summary Test Method::invoke and Constructor::newInstance chained calls that * should wrap NPE in InvocationTargetException properly + * @comment This test is not using assertThrows given lambdas may affect the + * exception stack trace and therefore test anticipations */ import java.lang.reflect.Constructor; @@ -33,24 +35,28 @@ import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.util.Optional; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; public class ChainedReflection { - public ChainedReflection() {} - ChainedReflection(Void dummy) throws ReflectiveOperationException { - Method m = ChainedReflection.class.getMethod("throwNPE"); - try { - m.invoke(null); - } catch (InvocationTargetException e) { - Throwable t = e.getTargetException(); - if (t instanceof NullPointerException npe) { - throw npe; - } else { - throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)"); + // This inner class is declared with a constructor that ctorCallMethodInvoke + // reflects on. Such a constructor cannot be declared in ChainedReflection + // because JUnit does not allow a test class to declare multiple constructors. + class Inner { + Inner() throws ReflectiveOperationException { + Method m = ChainedReflection.class.getMethod("throwNPE"); + try { + m.invoke(null); + } catch (InvocationTargetException e) { + Throwable t = e.getTargetException(); + if (t instanceof NullPointerException npe) { + throw npe; + } else { + throw new RuntimeException("Test failed (InvocationTargetException didn't wrap NullPointerException)"); + } + } catch (Throwable t) { + throw new RuntimeException("Test failed (Unexpected exception)", t); } - } catch (Throwable t) { - throw new RuntimeException("Test failed (Unexpected exception)", t); } } @@ -105,9 +111,9 @@ public class ChainedReflection { */ @Test public void ctorCallMethodInvoke() throws ReflectiveOperationException { - Constructor ctor = ChainedReflection.class.getDeclaredConstructor(Void.class); + Constructor ctor = ChainedReflection.Inner.class.getDeclaredConstructor(ChainedReflection.class); try { - ctor.newInstance((Void)null); + ctor.newInstance(this); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (!(t instanceof NullPointerException)) { diff --git a/test/jdk/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java b/test/jdk/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java index 1992f31d5de..b8738a383b0 100644 --- a/test/jdk/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java +++ b/test/jdk/java/lang/reflect/DefaultMethodMembers/FilterNotMostSpecific.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -26,7 +26,7 @@ * @bug 8029674 * @summary Verify that the right interface methods are returned by * Class.getMethod() and Class.getMethods() - * @run testng FilterNotMostSpecific + * @run junit FilterNotMostSpecific */ import java.lang.reflect.*; @@ -39,14 +39,14 @@ import java.util.HashMap; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class FilterNotMostSpecific { - @Test(dataProvider="getCases") + @ParameterizedTest + @MethodSource("getCases") public void testGetMethod(Class iface) { boolean match = false; MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class); @@ -67,7 +67,8 @@ public class FilterNotMostSpecific { assert(match); } - @Test(dataProvider="getCases") + @ParameterizedTest + @MethodSource("getCases") public void testGetMethods(Class iface) { List foundMethods = filterObjectMethods(iface.getMethods()); MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class); @@ -84,7 +85,7 @@ public class FilterNotMostSpecific { fail("On: "+ iface +"\nDid not find " + toMethodString(expected) + " among " + foundMethods); } - assertEquals(foundMethods.size(), expectedMethods.length, + assertEquals(expectedMethods.length, foundMethods.size(), "\non: " + iface + "\nexpected: " + toMethodStrings(expectedMethods) + "\nfound: " + foundMethods + "\n"); @@ -565,8 +566,7 @@ public class FilterNotMostSpecific { @MethodDesc(name="m", declaringClass=F.class, isGetMethodReturn=true) abstract class H extends G implements F {} - @DataProvider - public Object[][] getCases() { return CASES; } + public static Object[][] getCases() { return CASES; } public static final Class[][] CASES = { { K1.class }, { K1M.class }, diff --git a/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java b/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java index 215ee3dd66a..ff7d12502b3 100644 --- a/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java +++ b/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticInvokeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 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,7 +27,7 @@ * in interfaces and/or in inheritance * @bug 7184826 * @build helper.Mod helper.Declared DefaultStaticTestData - * @run testng DefaultStaticInvokeTest + * @run junit DefaultStaticInvokeTest * @author Yong Lu */ @@ -40,23 +40,19 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.fail; -import org.testng.annotations.Test; - import static helper.Mod.*; import static helper.Declared.*; -import helper.Mod; +import static org.junit.jupiter.api.Assertions.*; +import helper.Mod; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class DefaultStaticInvokeTest { // getMethods(): Make sure getMethods returns the expected methods. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testGetMethods(String testTarget, Object param) throws Exception { testMethods(ALL_METHODS, testTarget, param); @@ -64,8 +60,8 @@ public class DefaultStaticInvokeTest { // getDeclaredMethods(): Make sure getDeclaredMethods returns the expected methods. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testGetDeclaredMethods(String testTarget, Object param) throws Exception { testMethods(DECLARED_ONLY, testTarget, param); @@ -73,8 +69,8 @@ public class DefaultStaticInvokeTest { // getMethod(): Make sure that getMethod finds all methods it should find. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testGetMethod(String testTarget, Object param) throws Exception { @@ -91,8 +87,8 @@ public class DefaultStaticInvokeTest { // getMethod(): Make sure that getMethod does *not* find certain methods. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testGetMethodSuperInterfaces(String testTarget, Object param) throws Exception { @@ -124,8 +120,8 @@ public class DefaultStaticInvokeTest { // Method.invoke(): Make sure Method.invoke returns the expected value. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testMethodInvoke(String testTarget, Object param) throws Exception { Class typeUnderTest = Class.forName(testTarget); @@ -141,8 +137,8 @@ public class DefaultStaticInvokeTest { // MethodHandle.invoke(): Make sure MethodHandle.invoke returns the expected value. - @Test(dataProvider = "testCasesAll", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource({"DefaultStaticTestData#testClasses", "DefaultStaticTestData#testInterfaces"}) public void testMethodHandleInvoke(String testTarget, Object param) throws Throwable { Class typeUnderTest = Class.forName(testTarget); @@ -169,14 +165,14 @@ public class DefaultStaticInvokeTest { : (String) methodHandle.invoke(typeUnderTest.newInstance(), param); } - assertEquals(result, expectedReturn); + assertEquals(expectedReturn, result); } } // Lookup.findStatic / .findVirtual: Make sure IllegalAccessException is thrown as expected. - @Test(dataProvider = "testClasses", - dataProviderClass = DefaultStaticTestData.class) + @ParameterizedTest + @MethodSource("DefaultStaticTestData#testClasses") public void testIAE(String testTarget, Object param) throws ClassNotFoundException { @@ -189,14 +185,8 @@ public class DefaultStaticInvokeTest { if (mod != STATIC && typeUnderTest.isInterface()) { continue; } - Exception caught = null; - try { - getTestMH(typeUnderTest, mName, param, true); - } catch (Exception e) { - caught = e; - } - assertNotNull(caught); - assertEquals(caught.getClass(), IllegalAccessException.class); + assertThrowsExactly(IllegalAccessException.class, () -> + getTestMH(typeUnderTest, mName, param, true)); } } @@ -254,7 +244,7 @@ public class DefaultStaticInvokeTest { } } - assertEquals(myMethods.size(), expectedMethods.length); + assertEquals(expectedMethods.length, myMethods.size()); for (MethodDesc toTest : expectedMethods) { @@ -284,16 +274,15 @@ public class DefaultStaticInvokeTest { assertFalse(method.isDefault()); // Test invoke it - assertEquals(tryInvoke(method, null, param), expectedReturn); + assertEquals(expectedReturn, tryInvoke(method, null, param)); break; case DEFAULT: // if typeUnderTest is a class then instantiate and invoke if (!typeUnderTest.isInterface()) { - assertEquals(tryInvoke( + assertEquals(expectedReturn, tryInvoke( method, typeUnderTest, - param), - expectedReturn); + param)); } //assert candidate is default @@ -302,11 +291,10 @@ public class DefaultStaticInvokeTest { break; case REGULAR: // if typeUnderTest must be a class - assertEquals(tryInvoke( + assertEquals(expectedReturn, tryInvoke( method, typeUnderTest, - param), - expectedReturn); + param)); //assert candidate is neither default nor static assertFalse(Modifier.isStatic(method.getModifiers())); diff --git a/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticTestData.java b/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticTestData.java index 8049712569b..add991e3493 100644 --- a/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticTestData.java +++ b/test/jdk/java/lang/reflect/DefaultStaticTest/DefaultStaticTestData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 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,12 +27,10 @@ * @author Yong Lu */ +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.testng.annotations.DataProvider; -import org.testng.collections.Lists; - import static helper.Mod.*; import static helper.Declared.*; import helper.Mod; @@ -379,7 +377,6 @@ public class DefaultStaticTestData { * data is the name of the class under test Second data used in test as the * arguments used for the method call. */ - @DataProvider static Object[][] testClasses() { return new Object[][]{ {"TestClass1", null}, @@ -408,7 +405,6 @@ public class DefaultStaticTestData { * data is the name of the interface under test Second data used in test as * the arguments used for the method call. */ - @DataProvider static Object[][] testInterfaces() { return new Object[][]{ {"TestIF1", null}, @@ -438,12 +434,4 @@ public class DefaultStaticTestData { {"TestIF21", null}, }; } - - @DataProvider - static Object[][] testCasesAll() { - List result = Lists.newArrayList(); - result.addAll(Arrays.asList(testClasses())); - result.addAll(Arrays.asList(testInterfaces())); - return result.toArray(new Object[result.size()][]); - } } diff --git a/test/jdk/java/lang/reflect/Field/NegativeTest.java b/test/jdk/java/lang/reflect/Field/NegativeTest.java index fe1f2dd7480..186267f4458 100644 --- a/test/jdk/java/lang/reflect/Field/NegativeTest.java +++ b/test/jdk/java/lang/reflect/Field/NegativeTest.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 @@ -21,20 +21,20 @@ * questions. */ -/** +/* * @test * @bug 8277451 * @summary Test exception thrown due to bad receiver and bad value on * Field with and without setAccessible(true) - * @run testng/othervm --enable-final-field-mutation=ALL-UNNAMED NegativeTest + * @run junit/othervm --enable-final-field-mutation=ALL-UNNAMED NegativeTest */ import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class NegativeTest { static class Fields { @@ -167,8 +167,7 @@ public class NegativeTest { } } - @DataProvider(name = "instanceFields") - private Object[][] instanceFields() { + private static Object[][] instanceFields() { return new Object[][]{ new Object[]{i_field}, new Object[]{c_field}, @@ -217,7 +216,8 @@ public class NegativeTest { * IllegalArgumentException is thrown if the receiver is of * a bad type. NullPointerException is thrown if the receiver is null. */ - @Test(dataProvider = "instanceFields") + @ParameterizedTest + @MethodSource("instanceFields") public void testReceiver(Field f) throws ReflectiveOperationException { f.get(INSTANCE); // good receiver @@ -231,15 +231,10 @@ public class NegativeTest { private void testBadReceiver(Field f) throws ReflectiveOperationException { assertFalse(Modifier.isStatic(f.getModifiers())); // instance field Object badObj = new NegativeTest(); - try { - f.get(badObj); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> f.get(badObj)); Class fType = f.getType(); if (fType.isPrimitive()) { - try { + assertThrows(IllegalArgumentException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.getByte(badObj); case "C" -> f.getChar(badObj); @@ -250,10 +245,7 @@ public class NegativeTest { case "S" -> f.getShort(badObj); case "Z" -> f.getBoolean(badObj); } - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } + }); } } @@ -262,16 +254,11 @@ public class NegativeTest { */ private void testNullReceiver(Field f) throws ReflectiveOperationException { assertFalse(Modifier.isStatic(f.getModifiers())); // instance field - try { - f.get(null); - fail("expected NullPointerException"); - } catch (NullPointerException e) { - // expected - } + assertThrows(NullPointerException.class, () -> f.get(null)); Class fType = f.getType(); if (fType.isPrimitive()) { - try { + assertThrows(NullPointerException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.getByte(null); case "C" -> f.getChar(null); @@ -282,15 +269,11 @@ public class NegativeTest { case "S" -> f.getShort(null); case "Z" -> f.getBoolean(null); } - fail("expected NullPointerException"); - } catch (NullPointerException e) { - // expected - } + }); } } - @DataProvider(name = "writeableFields") - private Object[][] writeableFields() { + private static Object[][] writeableFields() { Fields obj = new Fields(); return new Object[][]{ // instance fields with and without setAccessible(true) @@ -352,7 +335,8 @@ public class NegativeTest { * NullPointerException is thrown if the receiver of an instance field is null. * The receiver is checked */ - @Test(dataProvider = "writeableFields") + @ParameterizedTest + @MethodSource("writeableFields") public void testSetValue(Field f, Object obj, Object value) throws IllegalAccessException { f.set(obj, value); Class fType = f.getType(); @@ -369,25 +353,14 @@ public class NegativeTest { } // test null value only if it's primitive type - try { - f.set(obj, null); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> f.set(obj, null)); } Object badValue = new NegativeTest(); - try { - f.set(obj, badValue); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> f.set(obj, badValue)); } - @DataProvider(name = "readOnlyFinalFields") - private Object[][] readOnlyFinalFields() { + private static Object[][] readOnlyFinalFields() { Object obj = INSTANCE; return new Object[][]{ // instance final fields @@ -427,19 +400,15 @@ public class NegativeTest { * IllegalAccessException is thrown regardless of whether the value * is of a bad type or not. */ - @Test(dataProvider = "readOnlyFinalFields") + @ParameterizedTest + @MethodSource("readOnlyFinalFields") public void testSetValueOnFinalField(Field f, Object obj, Object value) { assertTrue(Modifier.isFinal(f.getModifiers())); - try { - f.set(obj, value); - fail("expected IllegalAccessException"); - } catch (IllegalAccessException e) { - // expected - } + assertThrows(IllegalAccessException.class, () -> f.set(obj, value)); Class fType = f.getType(); if (fType.isPrimitive()) { - try { + assertThrows(IllegalAccessException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.setByte(obj, ((Byte)value).byteValue()); case "C" -> f.setChar(obj, ((Character)value).charValue()); @@ -450,33 +419,17 @@ public class NegativeTest { case "S" -> f.setShort(obj, ((Short)value).shortValue()); case "Z" -> f.setBoolean(obj, ((Boolean)value).booleanValue()); } - fail("expected IllegalAccessException"); - } catch (IllegalAccessException e) { - // expected - } + }); // test null value only if it's primitive type - try { - f.set(obj, null); - fail("expected IllegalAccessException"); - } catch (IllegalAccessException e) { - // expected - } + assertThrows(IllegalAccessException.class, () -> f.set(obj, null)); } Object badValue = new NegativeTest(); - try { - f.set(obj, badValue); - fail("expected IllegalAccessException"); - } catch (IllegalAccessException e) { - // expected - } + assertThrows(IllegalAccessException.class, () -> f.set(obj, badValue)); } - - - @DataProvider(name = "finalInstanceFields") - private Object[][] finalInstanceFields() { + private static Object[][] finalInstanceFields() { return new Object[][]{ new Object[]{fi_field, Integer.valueOf(10)}, new Object[]{fc_field, Character.valueOf('c')}, @@ -497,54 +450,27 @@ public class NegativeTest { * The receiver is checked before the access check is performed and * also before the value is checked. */ - @Test(dataProvider = "finalInstanceFields") + @ParameterizedTest + @MethodSource("finalInstanceFields") public void testReceiverOnFinalField(Field f, Object value) { assertTrue(Modifier.isFinal(f.getModifiers())); Object badReceiver = new NegativeTest(); // set the field with a bad receiver with a good value - try { - f.set(badReceiver, value); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); - } + assertThrows(IllegalArgumentException.class, () -> f.set(badReceiver, value)); // set the field with a bad receiver with a bad value Object badValue = new NegativeTest(); - try { - f.set(badReceiver, badValue); - fail("expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); - } + assertThrows(IllegalArgumentException.class, () -> f.set(badReceiver, badValue)); // set the field with a null receiver with a good value - try { - f.set(null, value); - fail("expected NullPointerException"); - } catch (NullPointerException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); - } + assertThrows(NullPointerException.class, () -> f.set(null, value)); // set the field with a null receiver with a bad value - try { - f.set(null, badValue); - fail("expected NullPointerException"); - } catch (NullPointerException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); - } + assertThrows(NullPointerException.class, () -> f.set(null, badValue)); Class fType = f.getType(); if (fType.isPrimitive()) { // test bad receiver - try { + assertThrows(IllegalArgumentException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.setByte(badReceiver, ((Byte) value).byteValue()); case "C" -> f.setChar(badReceiver, ((Character) value).charValue()); @@ -555,12 +481,9 @@ public class NegativeTest { case "S" -> f.setShort(badReceiver, ((Short) value).shortValue()); case "Z" -> f.setBoolean(badReceiver, ((Boolean) value).booleanValue()); } - } catch (IllegalArgumentException e) { - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected IllegalArgumentException but got: " + e.getMessage(), e); - } + }); // test null receiver - try { + assertThrows(NullPointerException.class, () -> { switch (fType.descriptorString()) { case "B" -> f.setByte(null, ((Byte) value).byteValue()); case "C" -> f.setChar(null, ((Character) value).charValue()); @@ -571,11 +494,7 @@ public class NegativeTest { case "S" -> f.setShort(null, ((Short) value).shortValue()); case "Z" -> f.setBoolean(null, ((Boolean) value).booleanValue()); } - } catch (NullPointerException e) { - // expected - } catch (IllegalAccessException e) { - throw new RuntimeException("Expected NullPointerException but got: " + e.getMessage(), e); - } + }); } } } diff --git a/test/jdk/java/lang/reflect/Generics/ThreadSafety.java b/test/jdk/java/lang/reflect/Generics/ThreadSafety.java index 1aa1a4e9386..81ca2ae488a 100644 --- a/test/jdk/java/lang/reflect/Generics/ThreadSafety.java +++ b/test/jdk/java/lang/reflect/Generics/ThreadSafety.java @@ -21,17 +21,16 @@ * questions. */ -/** +/* * @test * @bug 8062771 8016236 * @summary Test publication of Class objects via a data race - * @run testng ThreadSafety + * @run junit ThreadSafety */ import java.io.File; import java.net.URL; import java.net.URLClassLoader; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.concurrent.BrokenBarrierException; @@ -43,8 +42,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.testng.Assert.*; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /** * A test resulting from an attempt to repro this failure (in guice): diff --git a/test/jdk/java/lang/reflect/IllegalArgumentsTest.java b/test/jdk/java/lang/reflect/IllegalArgumentsTest.java index 7260c04c10a..58115523a3d 100644 --- a/test/jdk/java/lang/reflect/IllegalArgumentsTest.java +++ b/test/jdk/java/lang/reflect/IllegalArgumentsTest.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 @@ -25,12 +25,14 @@ * @test * @bug 8277964 * @summary Test IllegalArgumentException be thrown when an argument is invalid - * @run testng/othervm/timeout=720 IllegalArgumentsTest + * @comment Avoid using framework utilities; this is effectively a runtime test + * sensitive to stack traces + * @run junit/othervm/timeout=720 IllegalArgumentsTest */ import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; public class IllegalArgumentsTest { static class T { diff --git a/test/jdk/java/lang/reflect/Method/MethodArityLimit.java b/test/jdk/java/lang/reflect/Method/MethodArityLimit.java index 536b3a5e317..05d4ef609ef 100644 --- a/test/jdk/java/lang/reflect/Method/MethodArityLimit.java +++ b/test/jdk/java/lang/reflect/Method/MethodArityLimit.java @@ -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 @@ -24,18 +24,16 @@ /* * @test * @bug 8271820 - * @run testng/othervm MethodArityLimit + * @run junit/othervm MethodArityLimit * @summary Method exceeds the method handle arity limit (255). */ -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import static org.junit.jupiter.api.Assertions.*; public class MethodArityLimit { @Test @@ -74,12 +72,10 @@ public class MethodArityLimit { 106L, 107L, 108L, 109L, 110L, 111L, 112L, 113L, 114L, 115L, 116L, 117L, 118L, 119L, 120L, 121L, 122L, 123L, 124L, 125L, 126L, 127); - assertEquals(resultViaMethod, 127); + assertEquals(127, resultViaMethod); - try { - MethodHandle mh = MethodHandles.lookup().unreflect(m); - fail("should fail in creating the method handle"); - } catch (IllegalArgumentException e) {} + var lookup = MethodHandles.lookup(); + assertThrows(IllegalArgumentException.class, () -> lookup.unreflect(m)); } public static long f(long a0, long a1, long a2, long a3, long a4, long a5, diff --git a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java index b7e6b52b5dc..af72d8dec58 100644 --- a/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java +++ b/test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, 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 @@ -26,7 +26,7 @@ * @bug 8271820 8300924 * @modules java.base/jdk.internal.reflect * @summary Test compliance of ConstructorAccessor, FieldAccessor, MethodAccessor implementations - * @run testng/othervm --add-exports java.base/jdk.internal.reflect=ALL-UNNAMED -XX:-ShowCodeDetailsInExceptionMessages MethodHandleAccessorsTest + * @run junit/othervm -XX:-ShowCodeDetailsInExceptionMessages MethodHandleAccessorsTest */ import jdk.internal.reflect.ConstructorAccessor; @@ -44,8 +44,11 @@ import java.util.Objects; import java.util.function.IntUnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertThrows; public class MethodHandleAccessorsTest { public static void public_static_V() {} @@ -446,9 +449,7 @@ public class MethodHandleAccessorsTest { new InvocationTargetException(new IllegalArgumentException("IAE")) }; - - @DataProvider(name = "testNoArgMethods") - private Object[][] testNoArgMethods() { + private static Object[][] testNoArgMethods() { MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest(); Object[] emptyArgs = new Object[]{}; return new Object[][] { @@ -468,8 +469,7 @@ public class MethodHandleAccessorsTest { }; } - @DataProvider(name = "testOneArgMethods") - private Object[][] testOneArgMethods() { + private static Object[][] testOneArgMethods() { MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest(); Object wrongInst = new Object(); return new Object[][]{ @@ -497,8 +497,7 @@ public class MethodHandleAccessorsTest { }; } - @DataProvider(name = "testMultiArgMethods") - private Object[][] testMultiArgMethods() { + private static Object[][] testMultiArgMethods() { MethodHandleAccessorsTest inst = new MethodHandleAccessorsTest(); Class[] params_L3 = new Class[] { Object.class, Object.class, Object.class}; Class[] params_L4 = new Class[] { Object.class, Object.class, Object.class, Object.class}; @@ -515,8 +514,7 @@ public class MethodHandleAccessorsTest { }; } - @DataProvider(name = "testMethodsWithVarargs") - private Object[][] testMethodsWithVarargs() { + private static Object[][] testMethodsWithVarargs() { Class[] paramTypes = new Class[] { int[].class }; Class[] I_paramTypes = new Class[] { int.class, int[].class }; Class[] L_paramTypes = new Class[] { String.class, String[].class }; @@ -533,32 +531,35 @@ public class MethodHandleAccessorsTest { }; } - @Test(dataProvider = "testNoArgMethods") + @ParameterizedTest + @MethodSource("testNoArgMethods") public void testNoArgMethod(String methodname, Object target, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname), target, args, expectedReturn, expectedExpections); } - @Test(dataProvider = "testOneArgMethods") + @ParameterizedTest + @MethodSource("testOneArgMethods") public void testOneArgMethod(String methodname, Class paramType, Object target, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramType), target, args, expectedReturn, expectedExpections); } - @Test(dataProvider = "testMultiArgMethods") + @ParameterizedTest + @MethodSource("testMultiArgMethods") public void testMultiArgMethod(String methodname, Class[] paramTypes, Object target, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramTypes), target, args, expectedReturn, expectedExpections); } - @Test(dataProvider = "testMethodsWithVarargs") + @ParameterizedTest + @MethodSource("testMethodsWithVarargs") public void testMethodsWithVarargs(String methodname, Class[] paramTypes, Object target, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(MethodHandleAccessorsTest.class.getDeclaredMethod(methodname, paramTypes), target, args, expectedReturn, expectedExpections); } - @DataProvider(name = "testConstructors") - private Object[][] testConstructors() { + private static Object[][] testConstructors() { return new Object[][]{ new Object[]{null, new Object[]{}, new Public(), noException}, new Object[]{null, null, new Public(), noException}, @@ -584,13 +585,13 @@ public class MethodHandleAccessorsTest { }; } - @Test(dataProvider = "testConstructors") + @ParameterizedTest + @MethodSource("testConstructors") public void testPublicConstructors(Class[] paramTypes, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(Public.class.getDeclaredConstructor(paramTypes), args, expectedReturn, expectedExpections); } - @DataProvider(name = "testMultiArgConstructors") - private Object[][] testMultiArgConstructors() { + private static Object[][] testMultiArgConstructors() { Class[] params_L3 = new Class[] { Object.class, Object.class, Object.class}; Class[] params_L4 = new Class[] { Object.class, Object.class, Object.class, Object.class}; Object o = "arg"; @@ -602,7 +603,8 @@ public class MethodHandleAccessorsTest { }; } - @Test(dataProvider = "testMultiArgConstructors") + @ParameterizedTest + @MethodSource("testMultiArgConstructors") public void testMultiArgConstructors(Class[] paramTypes, Object[] args, Object expectedReturn, Throwable[] expectedExpections) throws Exception { doTest(Public.class.getDeclaredConstructor(paramTypes), args, expectedReturn, expectedExpections); } @@ -616,8 +618,7 @@ public class MethodHandleAccessorsTest { doTest(Abstract.class.getDeclaredConstructor(), null, null, new InstantiationException()); } - @DataProvider(name = "throwException") - private Object[][] throwException() { + private static Object[][] throwException() { return new Object[][]{ new Object[] {new NullPointerException("NPE"), wrapped_npe}, new Object[] {new IllegalArgumentException("IAE"), wrapped_iae}, @@ -629,7 +630,8 @@ public class MethodHandleAccessorsTest { * Test Method::invoke and Constructor::newInstance to wrap NPE/CCE/IAE * thrown by the member */ - @Test(dataProvider = "throwException") + @ParameterizedTest + @MethodSource("throwException") public void testInvocationTargetException(Throwable ex, Throwable[] expectedExpections) throws Exception { Object[] args = new Object[] { ex }; // test static method @@ -646,8 +648,7 @@ public class MethodHandleAccessorsTest { doTest(applyAsIntMethod, intUnaryOp, new Object[]{12}, 12); } - @DataProvider(name = "readAccess") - private Object[][] readAccess() { + private static Object[][] readAccess() { String wrongInst = new String(); return new Object[][]{ new Object[]{"i", new Public(100), 100, noException}, @@ -657,8 +658,7 @@ public class MethodHandleAccessorsTest { new Object[]{"b", wrongInst, 0, cannot_get_field}, }; } - @DataProvider(name = "writeAccess") - private Object[][] writeAccess() { + private static Object[][] writeAccess() { Object o = new Object(); byte b = 1; return new Object[][]{ @@ -673,14 +673,16 @@ public class MethodHandleAccessorsTest { }; } - @Test(dataProvider = "readAccess") + @ParameterizedTest + @MethodSource("readAccess") public void testFieldReadAccess(String name, Object target, Object expectedValue, Throwable[] expectedExpections) throws Exception { Field f = Public.class.getDeclaredField(name); f.setAccessible(true); doTest(f, target, expectedValue, expectedExpections); } - @Test(dataProvider = "writeAccess") + @ParameterizedTest + @MethodSource("writeAccess") public void testFieldWriteAccess(String name, Object target, Object oldValue, Object newValue, Throwable[] expectedExpections) throws Exception { Field f = Public.class.getDeclaredField(name); f.setAccessible(true); @@ -693,8 +695,6 @@ public class MethodHandleAccessorsTest { Field f = Public.class.getDeclaredField("STATIC_FINAL"); doTest(f, new Public(), 1, noException); - try { - f.setInt(null, 100); - } catch (IllegalAccessException e) { } + assertThrows(IllegalAccessException.class, () -> f.setInt(null, 100)); } } diff --git a/test/jdk/java/lang/reflect/Proxy/DefaultMethods.java b/test/jdk/java/lang/reflect/Proxy/DefaultMethods.java index 48bb67f405e..e494e623119 100644 --- a/test/jdk/java/lang/reflect/Proxy/DefaultMethods.java +++ b/test/jdk/java/lang/reflect/Proxy/DefaultMethods.java @@ -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,19 +23,19 @@ import java.io.IOException; import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -/** +/* * @test * @bug 8159746 - * @run testng DefaultMethods + * @run junit DefaultMethods * @summary Basic tests for Proxy::invokeSuper default method */ @@ -96,15 +96,11 @@ public class DefaultMethods { } private static Method findDefaultMethod(Class refc, Method m) { - try { - assertTrue(refc.isInterface()); + assertTrue(refc.isInterface()); - Method method = refc.getMethod(m.getName(), m.getParameterTypes()); - assertTrue(method.isDefault()); - return method; - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } + Method method = assertDoesNotThrow(() -> refc.getMethod(m.getName(), m.getParameterTypes())); + assertTrue(method.isDefault()); + return method; } @Test @@ -115,12 +111,11 @@ public class DefaultMethods { return InvocationHandler.invokeDefault(o, findDefaultMethod(I2.class, method), params); }); I1 i1 = (I1) proxy; - assertEquals(i1.m(), 20); + assertEquals(20, i1.m()); } // a default method is declared in one of the proxy interfaces - @DataProvider(name = "defaultMethods") - private Object[][] defaultMethods() { + private static Object[][] defaultMethods() { return new Object[][]{ new Object[]{new Class[]{I1.class, I2.class}, true, 10}, new Object[]{new Class[]{I1.class, I3.class}, true, 10}, @@ -133,13 +128,14 @@ public class DefaultMethods { }; } - @Test(dataProvider = "defaultMethods") + @ParameterizedTest + @MethodSource("defaultMethods") public void testDefaultMethod(Class[] intfs, boolean isDefault, int expected) throws Throwable { InvocationHandler ih = (proxy, method, params) -> { System.out.format("invoking %s with parameters: %s%n", method, Arrays.toString(params)); switch (method.getName()) { case "m": - assertTrue(method.isDefault() == isDefault); + assertEquals(isDefault, method.isDefault()); assertTrue(Arrays.stream(proxy.getClass().getInterfaces()) .anyMatch(intf -> method.getDeclaringClass() == intf), Arrays.toString(proxy.getClass().getInterfaces())); @@ -156,13 +152,12 @@ public class DefaultMethods { Object proxy = Proxy.newProxyInstance(DefaultMethods.class.getClassLoader(), intfs, ih); Method m = proxy.getClass().getMethod("m"); int result = (int)m.invoke(proxy); - assertEquals(result, expected); + assertEquals(expected, result); } // a default method may be declared in a proxy interface or // inherited from a superinterface of a proxy interface - @DataProvider(name = "supers") - private Object[][] supers() { + private static Object[][] supers() { return new Object[][]{ // invoke "m" implemented in the first proxy interface // same as the method passed to InvocationHandler::invoke @@ -188,7 +183,8 @@ public class DefaultMethods { }; } - @Test(dataProvider = "supers") + @ParameterizedTest + @MethodSource("supers") public void testSuper(Class[] intfs, Class proxyInterface, int expected) throws Throwable { final InvocationHandler ih = (proxy, method, params) -> { switch (method.getName()) { @@ -203,21 +199,21 @@ public class DefaultMethods { Object proxy = Proxy.newProxyInstance(loader, intfs, ih); if (proxyInterface == I1.class) { I1 i1 = (I1) proxy; - assertEquals(i1.m(), expected); + assertEquals(expected, i1.m()); } else if (proxyInterface == I2.class) { I2 i2 = (I2) proxy; - assertEquals(i2.m(), expected); + assertEquals(expected, i2.m()); } else if (proxyInterface == I3.class) { I3 i3 = (I3) proxy; - assertEquals(i3.m(), expected); + assertEquals(expected, i3.m()); } else if (proxyInterface == I4.class) { I4 i4 = (I4) proxy; - assertEquals(i4.m(), expected); + assertEquals(expected, i4.m()); } else { throw new UnsupportedOperationException(proxyInterface.toString()); } // invoke via InvocationHandler.invokeDefaultMethod directly - assertEquals(InvocationHandler.invokeDefault(proxy, proxyInterface.getMethod("m")), expected); + assertEquals(expected, InvocationHandler.invokeDefault(proxy, proxyInterface.getMethod("m"))); } // invoke I12 default methods with parameters and var args @@ -236,12 +232,12 @@ public class DefaultMethods { }; ClassLoader loader = DefaultMethods.class.getClassLoader(); I12 i12 = (I12) Proxy.newProxyInstance(loader, new Class[] { I12.class }, ih); - assertEquals(i12.sum(1, 2), 3); - assertEquals(i12.concat(1, 2, 3, 4), new Object[]{1, 2, 3, 4}); + assertEquals(3, i12.sum(1, 2)); + assertArrayEquals(new Object[]{1, 2, 3, 4}, i12.concat(1, 2, 3, 4)); Method m = I12.class.getMethod("concat", Object.class, Object[].class); assertTrue(m.isDefault()); - assertEquals(InvocationHandler.invokeDefault(i12, m, 100, new Object[] {"foo", true, "bar"}), - new Object[] {100, "foo", true, "bar"}); + assertArrayEquals(new Object[] {100, "foo", true, "bar"}, (Object[]) + InvocationHandler.invokeDefault(i12, m, 100, new Object[] {"foo", true, "bar"})); } // test a no-arg default method with and without arguments passed in the invocation @@ -250,13 +246,13 @@ public class DefaultMethods { ClassLoader loader = DefaultMethods.class.getClassLoader(); Object proxy = Proxy.newProxyInstance(loader, new Class[]{I4.class}, HANDLER); Method m1 = I4.class.getMethod("m"); - assertTrue(m1.getDeclaringClass() == I4.class); + assertSame(I4.class, m1.getDeclaringClass()); assertTrue(m1.isDefault()); InvocationHandler.invokeDefault(proxy, m1); InvocationHandler.invokeDefault(proxy, m1, new Object[0]); Method m2 = I4.class.getMethod("mix", int.class, String.class); - assertTrue(m1.getDeclaringClass() == I4.class); + assertSame(I4.class, m1.getDeclaringClass()); assertTrue(m1.isDefault()); InvocationHandler.invokeDefault(proxy, m2, Integer.valueOf(100), "foo"); } @@ -267,38 +263,37 @@ public class DefaultMethods { I3 proxy = (I3)Proxy.newProxyInstance(loader, new Class[]{I3.class}, HANDLER); Method m = I3.class.getMethod("m3", String[].class); assertTrue(m.isVarArgs() && m.isDefault()); - assertEquals(proxy.m3("a", "b", "cde"), 5); - assertEquals(InvocationHandler.invokeDefault(proxy, m, (Object)new String[] { "a", "bc" }), 3); + assertEquals(5, proxy.m3("a", "b", "cde")); + assertEquals(3, InvocationHandler.invokeDefault(proxy, m, (Object)new String[] { "a", "bc" })); } /* * Invoke I12::m which is an abstract method */ - @Test(expectedExceptions = {IllegalArgumentException.class}) + @Test public void invokeAbstractMethod() throws Exception { ClassLoader loader = DefaultMethods.class.getClassLoader(); I12 proxy = (I12) Proxy.newProxyInstance(loader, new Class[]{I12.class}, HANDLER); Method method = I12.class.getMethod("m"); - assertTrue(method.getDeclaringClass() == I12.class); + assertSame(I12.class, method.getDeclaringClass()); assertFalse(method.isDefault()); - proxy.m(); + assertThrows(IllegalArgumentException.class, () -> proxy.m()); } /* * Invoke a non proxy (default) method with parameters */ - @Test(expectedExceptions = {IllegalArgumentException.class}) + @Test public void invokeNonProxyMethod() throws Throwable { ClassLoader loader = DefaultMethods.class.getClassLoader(); I3 proxy = (I3) Proxy.newProxyInstance(loader, new Class[]{I3.class}, HANDLER); Method m = I4.class.getMethod("mix", int.class, String.class); assertTrue(m.isDefault()); - InvocationHandler.invokeDefault(proxy, m); + assertThrows(IllegalArgumentException.class, () -> InvocationHandler.invokeDefault(proxy, m)); } // negative cases - @DataProvider(name = "negativeCases") - private Object[][] negativeCases() { + private static Object[][] negativeCases() { return new Object[][]{ // I4::m overrides I1::m and I2::m new Object[] { new Class[]{I4.class}, I1.class, "m" }, @@ -315,25 +310,20 @@ public class DefaultMethods { }; } - @Test(dataProvider = "negativeCases", expectedExceptions = {IllegalArgumentException.class}) + @ParameterizedTest + @MethodSource("negativeCases") public void testNegativeCase(Class[] interfaces, Class defc, String name) throws Throwable { ClassLoader loader = DefaultMethods.class.getClassLoader(); Object proxy = Proxy.newProxyInstance(loader, interfaces, HANDLER); - try { - Method method = defc.getDeclaredMethod(name); - InvocationHandler.invokeDefault(proxy, method); - } catch (Throwable e) { - System.out.format("%s method %s::%s exception thrown: %s%n", - Arrays.toString(interfaces), defc.getName(), name, e.getMessage()); - throw e; - } + Method method = defc.getDeclaredMethod(name); + assertThrows(IllegalArgumentException.class, () -> + InvocationHandler.invokeDefault(proxy, method)); } - @DataProvider(name = "illegalArguments") - private Object[][] illegalArguments() { - return new Object[][] { - new Object[] { new Object[0]}, + private static Object[] illegalArguments() { + return new Object[] { + new Object[] { null }, new Object[] { new Object[] { 100 }}, new Object[] { new Object[] { 100, "foo", 100 }}, new Object[] { new Object[] { 100L, "foo" }}, @@ -342,21 +332,18 @@ public class DefaultMethods { }; } - @Test(dataProvider = "illegalArguments", expectedExceptions = {IllegalArgumentException.class}) + @ParameterizedTest + @MethodSource("illegalArguments") public void testIllegalArgument(Object[] args) throws Throwable { ClassLoader loader = DefaultMethods.class.getClassLoader(); I4 proxy = (I4)Proxy.newProxyInstance(loader, new Class[]{I4.class}, HANDLER); Method m = I4.class.getMethod("mix", int.class, String.class); assertTrue(m.isDefault()); - if (args.length == 0) { - // substitute empty args with null since @DataProvider doesn't allow null array - args = null; - } - InvocationHandler.invokeDefault(proxy, m, args); + assertThrows(IllegalArgumentException.class, () -> + InvocationHandler.invokeDefault(proxy, m, args)); } - @DataProvider(name = "throwables") - private Object[][] throwables() { + private static Object[][] throwables() { return new Object[][] { new Object[] { new IOException() }, new Object[] { new IllegalArgumentException() }, @@ -367,16 +354,14 @@ public class DefaultMethods { }; } - @Test(dataProvider = "throwables") + @ParameterizedTest + @MethodSource("throwables") public void testInvocationException(Throwable exception) throws Throwable { ClassLoader loader = DefaultMethods.class.getClassLoader(); IX proxy = (IX)Proxy.newProxyInstance(loader, new Class[]{IX.class}, HANDLER); Method m = IX.class.getMethod("doThrow", Throwable.class); - try { - InvocationHandler.invokeDefault(proxy, m, exception); - } catch (Throwable e) { - assertEquals(e, exception); - } + assertSame(exception, assertThrows(Throwable.class, () -> + InvocationHandler.invokeDefault(proxy, m, exception))); } private static final InvocationHandler HANDLER = (proxy, method, params) -> { diff --git a/test/jdk/java/lang/reflect/Proxy/HiddenProxyInterface.java b/test/jdk/java/lang/reflect/Proxy/HiddenProxyInterface.java index e52d3671ab6..fbaf9db6325 100644 --- a/test/jdk/java/lang/reflect/Proxy/HiddenProxyInterface.java +++ b/test/jdk/java/lang/reflect/Proxy/HiddenProxyInterface.java @@ -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 @@ -24,7 +24,7 @@ /* * @test * @bug 8250219 - * @run testng HiddenProxyInterface + * @run junit HiddenProxyInterface */ import java.lang.invoke.MethodHandles; @@ -35,8 +35,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class HiddenProxyInterface { private static final Path CLASSES_DIR = Paths.get(System.getProperty("test.classes")); @@ -45,18 +45,19 @@ public class HiddenProxyInterface { void m(); } - @Test(expectedExceptions = { IllegalArgumentException.class }) + @Test public void testHiddenInterface() throws Exception { Path classFile = CLASSES_DIR.resolve("HiddenProxyInterface$Intf.class"); byte[] bytes = Files.readAllBytes(classFile); Class hiddenIntf = MethodHandles.lookup().defineHiddenClass(bytes, false).lookupClass(); - Proxy.newProxyInstance(HiddenProxyInterface.class.getClassLoader(), + assertThrows(IllegalArgumentException.class, () -> Proxy.newProxyInstance( + HiddenProxyInterface.class.getClassLoader(), new Class[]{ hiddenIntf }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } - }); + })); } } diff --git a/test/jdk/java/lang/reflect/Proxy/LazyInitializationTest.java b/test/jdk/java/lang/reflect/Proxy/LazyInitializationTest.java index cb65904cfdb..2a223873275 100644 --- a/test/jdk/java/lang/reflect/Proxy/LazyInitializationTest.java +++ b/test/jdk/java/lang/reflect/Proxy/LazyInitializationTest.java @@ -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 @@ -23,14 +23,15 @@ import java.lang.reflect.Proxy; -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; /* * @test * @bug 8285401 * @summary Avoid initialization of parameter types in proxy construction - * @run testng LazyInitializationTest + * @run junit LazyInitializationTest */ public final class LazyInitializationTest { private static volatile boolean initialized = false; @@ -50,9 +51,9 @@ public final class LazyInitializationTest { Intf value = (Intf) Proxy.newProxyInstance(LazyInitializationTest.class.getClassLoader(), new Class[]{ Intf.class }, (proxy, method, args) -> null); - Assert.assertFalse(initialized, "parameter type initialized unnecessarily"); + assertFalse(initialized, "parameter type initialized unnecessarily"); value.m(new Parameter()); - Assert.assertTrue(initialized, "parameter type initialized after instantiation"); + assertTrue(initialized, "parameter type initialized after instantiation"); } } diff --git a/test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java b/test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java index 50fbd150ec7..265f9ce7104 100644 --- a/test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java +++ b/test/jdk/java/lang/reflect/Proxy/ProxyClassAccessTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, 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 @@ -32,17 +32,17 @@ import java.util.List; import jdk.test.lib.compiler.CompilerUtils; import static jdk.test.lib.process.ProcessTools.executeTestJava; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -/** +/* * @test * @library /test/lib * @modules jdk.compiler * @build ProxyClassAccessTest q.NP * jdk.test.lib.compiler.CompilerUtils - * @run testng ProxyClassAccessTest + * @run junit ProxyClassAccessTest * @summary Driver for testing proxy class doesn't have access to * types referenced by proxy interfaces */ @@ -61,8 +61,8 @@ public class ProxyClassAccessTest { /** * Compiles all modules used by the test */ - @BeforeTest - public void compileAll() throws Exception { + @BeforeAll + public static void compileAll() throws Exception { for (String mn : modules) { Path msrc = SRC_DIR.resolve(mn); assertTrue(CompilerUtils.compile(msrc, MODS_DIR, "--module-source-path", SRC_DIR.toString())); @@ -80,7 +80,7 @@ public class ProxyClassAccessTest { .errorTo(System.out) .getExitValue(); - assertTrue(exitValue == 0); + assertEquals(0, exitValue); } /** @@ -105,16 +105,10 @@ public class ProxyClassAccessTest { } private void checkIAE(ClassLoader loader, Class[] interfaces) throws Throwable { - try { - Proxy.getProxyClass(loader, interfaces); - throw new RuntimeException("Expected IllegalArgumentException thrown"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> Proxy.getProxyClass(loader, interfaces)); - try { - Proxy.newProxyInstance(loader, interfaces, - (proxy, m, params) -> { throw new RuntimeException(m.toString()); }); - throw new RuntimeException("Expected IllegalArgumentException thrown"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> Proxy.newProxyInstance(loader, interfaces, + (proxy, m, params) -> { throw new RuntimeException(m.toString()); })); } } diff --git a/test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java b/test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java index 85ebf922a20..d2708e18c0e 100644 --- a/test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java +++ b/test/jdk/java/lang/reflect/Proxy/ProxyLayerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, 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 @@ -32,17 +32,18 @@ import java.util.Arrays; import jdk.test.lib.compiler.CompilerUtils; import static jdk.test.lib.process.ProcessTools.executeTestJava; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; -/** +/* * @test * @library /test/lib * @modules jdk.compiler * @build ProxyTest jdk.test.lib.process.ProcessTools * jdk.test.lib.compiler.CompilerUtils - * @run testng ProxyLayerTest + * @run junit ProxyLayerTest * @summary Test proxies to implement interfaces in a layer */ @@ -62,8 +63,8 @@ public class ProxyLayerTest { /** * Compiles all modules used by the test */ - @BeforeTest - public void compileAll() throws Exception { + @BeforeAll + public static void compileAll() throws Exception { for (String mn : modules) { Path msrc = SRC_DIR.resolve(mn); assertTrue(CompilerUtils.compile(msrc, MODS_DIR, "--module-source-path", SRC_DIR.toString())); @@ -102,7 +103,7 @@ public class ProxyLayerTest { assertTrue(proxyClass.getModule().isNamed()); assertTrue(pkg.isSealed()); assertTrue(proxyClass.getModule().isExported(pkg.getName())); - assertEquals(proxyClass.getModule().getLayer(), null); + assertNull(proxyClass.getModule().getLayer()); } /** @@ -134,7 +135,7 @@ public class ProxyLayerTest { assertTrue(proxyClass.getModule().isNamed()); assertTrue(pkg.isSealed()); assertFalse(proxyClass.getModule().isExported(pkg.getName())); - assertEquals(proxyClass.getModule().getLayer(), null); + assertNull(proxyClass.getModule().getLayer()); } /** @@ -164,15 +165,8 @@ public class ProxyLayerTest { } private void checkIAE(ClassLoader loader, Class[] interfaces) { - try { - Proxy.getProxyClass(loader, interfaces); - throw new RuntimeException("Expected IllegalArgumentException thrown"); - } catch (IllegalArgumentException e) {} - - try { - Proxy.newProxyInstance(loader, interfaces, handler); - throw new RuntimeException("Expected IllegalArgumentException thrown"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> Proxy.getProxyClass(loader, interfaces)); + assertThrows(IllegalArgumentException.class, () -> Proxy.newProxyInstance(loader, interfaces, handler)); } private final static InvocationHandler handler = diff --git a/test/jdk/java/lang/reflect/Proxy/ProxyTest.java b/test/jdk/java/lang/reflect/Proxy/ProxyTest.java index 811471585a9..1da6a10260a 100644 --- a/test/jdk/java/lang/reflect/Proxy/ProxyTest.java +++ b/test/jdk/java/lang/reflect/Proxy/ProxyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, 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,17 +29,17 @@ import java.util.List; import jdk.test.lib.compiler.CompilerUtils; import static jdk.test.lib.process.ProcessTools.executeTestJava; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -/** +/* * @test * @library /test/lib * @modules jdk.compiler * @build ProxyTest q.U * jdk.test.lib.compiler.CompilerUtils - * @run testng ProxyTest + * @run junit ProxyTest * @summary Driver for testing proxies accessing interfaces in named modules */ @@ -59,8 +59,8 @@ public class ProxyTest { /** * Compiles all modules used by the test */ - @BeforeTest - public void compileAll() throws Exception { + @BeforeAll + public static void compileAll() throws Exception { for (String mn : modules) { Path msrc = SRC_DIR.resolve(mn); assertTrue(CompilerUtils.compile(msrc, MODS_DIR, "--module-source-path", SRC_DIR.toString())); @@ -79,7 +79,7 @@ public class ProxyTest { .errorTo(System.out) .getExitValue(); - assertTrue(exitValue == 0); + assertEquals(0, exitValue); } /** @@ -95,6 +95,6 @@ public class ProxyTest { .errorTo(System.out) .getExitValue(); - assertTrue(exitValue == 0); + assertEquals(0, exitValue); } } diff --git a/test/jdk/java/lang/reflect/Proxy/SealedInterfaceTest.java b/test/jdk/java/lang/reflect/Proxy/SealedInterfaceTest.java index 1337248f817..822c5d83319 100644 --- a/test/jdk/java/lang/reflect/Proxy/SealedInterfaceTest.java +++ b/test/jdk/java/lang/reflect/Proxy/SealedInterfaceTest.java @@ -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 @@ -24,15 +24,15 @@ /* * @test * @bug 8269351 - * @run testng SealedInterfaceTest + * @run junit SealedInterfaceTest */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class SealedInterfaceTest { sealed interface Intf permits NonSealedInterface { @@ -43,16 +43,17 @@ public class SealedInterfaceTest { void m2(); } - @Test(expectedExceptions = { IllegalArgumentException.class }) + @Test public void testSealedInterface() { - Proxy.newProxyInstance(SealedInterfaceTest.class.getClassLoader(), + assertThrows(IllegalArgumentException.class, () -> Proxy.newProxyInstance( + SealedInterfaceTest.class.getClassLoader(), new Class[]{ Intf.class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } - }); + })); } @Test diff --git a/test/jdk/java/lang/reflect/Proxy/TestVarArgs.java b/test/jdk/java/lang/reflect/Proxy/TestVarArgs.java index a472f75d4b7..b16831c7867 100644 --- a/test/jdk/java/lang/reflect/Proxy/TestVarArgs.java +++ b/test/jdk/java/lang/reflect/Proxy/TestVarArgs.java @@ -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 @@ -21,10 +21,10 @@ * questions. */ -/** +/* * @test * @bug 8022795 - * @run testng TestVarArgs + * @run junit TestVarArgs * @summary Verify if a method defined in a proxy interface has ACC_VARARGS set */ @@ -34,11 +34,10 @@ import java.lang.invoke.MethodType; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.util.Arrays; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class TestVarArgs { interface I { @@ -48,8 +47,7 @@ public class TestVarArgs { Object call(Object[] values); } - @DataProvider(name = "proxyInterfaces") - public Object[][] proxyInterfaces() { + public static Object[][] proxyInterfaces() { return new Object[][] { { new Class[] { I.class }, true}, { new Class[] { J.class }, false}, @@ -58,11 +56,12 @@ public class TestVarArgs { }; } - @Test(dataProvider = "proxyInterfaces") + @ParameterizedTest + @MethodSource("proxyInterfaces") public void testMethod(Class[] proxyInterfaces, boolean isVarArgs) throws Throwable { // check if the first proxy interface with the method named "call" declares var result Method m = proxyInterfaces[0].getMethod("call", Object[].class); - assertTrue(m.isVarArgs() == isVarArgs); + assertEquals(isVarArgs, m.isVarArgs()); // the method in the generated proxy class should match the method // declared in the proxy interface @@ -71,19 +70,19 @@ public class TestVarArgs { Class proxyClass = proxy.getClass(); assertTrue(Proxy.isProxyClass(proxyClass)); Method method = proxyClass.getMethod("call", Object[].class); - assertTrue(method.isVarArgs() == isVarArgs); + assertEquals(isVarArgs, method.isVarArgs()); - Object params = new Object[] { "foo", "bar", "goo" }; - Object result; + Object[] params = new Object[] { "foo", "bar", "goo" }; + Object[] result; // test reflection - result = method.invoke(proxy, params); - assertEquals(result, params); + result = (Object[]) method.invoke(proxy, new Object[] {params}); + assertEquals(params, result); // test method handle MethodHandle mh = MethodHandles.lookup().findVirtual(proxyClass, "call", MethodType.methodType(Object.class, Object[].class)); - assertTrue(mh.isVarargsCollector() == isVarArgs); + assertEquals(isVarArgs, mh.isVarargsCollector()); MethodHandle mhVarArity = mh; MethodHandle mhFixedArity = mh; if (isVarArgs) { @@ -91,18 +90,18 @@ public class TestVarArgs { } else { mhVarArity = mh.asVarargsCollector(Object[].class); } - result = mhVarArity.invoke(proxy, "foo", "bar", "goo"); - assertEquals(result, params); + result = (Object[]) mhVarArity.invoke(proxy, "foo", "bar", "goo"); + assertArrayEquals(params, result); - result = mhFixedArity.invoke(proxy, params); - assertEquals(result, params); + result = (Object[]) mhFixedArity.invoke(proxy, params); + assertArrayEquals(params, result); if (!isVarArgs) { MethodType mt = MethodType.methodType(Object.class, Object.class, String.class, String.class, String.class); mh = mh.asVarargsCollector(Object[].class).asType(mt); } - result = mh.invoke(proxy, "foo", "bar", "goo"); - assertEquals(result, params); + result = (Object[]) mh.invoke(proxy, "foo", "bar", "goo"); + assertArrayEquals(params, result); } private static final InvocationHandler IH = new InvocationHandler() { diff --git a/test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java b/test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java index daf6f656aab..a304063638c 100644 --- a/test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java +++ b/test/jdk/java/lang/reflect/Proxy/nonPublicProxy/DefaultMethodProxy.java @@ -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 @@ -25,16 +25,17 @@ import java.lang.reflect.*; import java.util.Arrays; import java.util.stream.Collectors; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /* * @test * @bug 8159746 * @summary Test invoking a default method in a non-public proxy interface * @build p.Foo p.Bar p.ProxyMaker - * @run testng DefaultMethodProxy + * @run junit DefaultMethodProxy */ public class DefaultMethodProxy { public interface I { @@ -42,7 +43,7 @@ public class DefaultMethodProxy { } @Test - public static void publicInterface() throws ReflectiveOperationException { + public void publicInterface() throws ReflectiveOperationException { // create a proxy instance of a public proxy interface should succeed Proxy proxy = (Proxy)Proxy.newProxyInstance(DefaultMethodProxy.class.getClassLoader(), new Class[] { I.class }, IH); @@ -50,11 +51,9 @@ public class DefaultMethodProxy { testDefaultMethod(proxy, "I"); // can get the invocation handler - assertTrue(Proxy.getInvocationHandler(proxy) == IH); + assertSame(IH, Proxy.getInvocationHandler(proxy)); } - - @DataProvider(name = "nonPublicIntfs") private static Object[][] nonPublicIntfs() throws ClassNotFoundException { Class fooClass = Class.forName("p.Foo"); Class barClass = Class.forName("p.Bar"); @@ -65,8 +64,9 @@ public class DefaultMethodProxy { }; } - @Test(dataProvider = "nonPublicIntfs") - public static void hasPackageAccess(Class[] intfs, String expected) throws ReflectiveOperationException { + @ParameterizedTest + @MethodSource("nonPublicIntfs") + public void hasPackageAccess(Class[] intfs, String expected) throws ReflectiveOperationException { Proxy proxy = (Proxy)Proxy.newProxyInstance(DefaultMethodProxy.class.getClassLoader(), intfs, IH); testDefaultMethod(proxy, expected); @@ -75,19 +75,13 @@ public class DefaultMethodProxy { } // IAE thrown at invocation time - @Test(dataProvider = "nonPublicIntfs", expectedExceptions = {IllegalAccessException.class}) - public static void noPackageAccess(Class[] intfs, String ignored) throws Throwable { + @ParameterizedTest + @MethodSource("nonPublicIntfs") + public void noPackageAccess(Class[] intfs, String ignored) throws Throwable { Proxy proxy = (Proxy)Proxy.newProxyInstance(DefaultMethodProxy.class.getClassLoader(), intfs, IH_NO_ACCESS); - try { - testDefaultMethod(proxy, "dummy"); - } catch (InvocationTargetException e) { - // unwrap the exception - if (e.getCause() instanceof UndeclaredThrowableException) { - Throwable cause = e.getCause(); - throw cause.getCause(); - } - throw e; - } + InvocationTargetException ite = assertThrows(InvocationTargetException.class, () -> testDefaultMethod(proxy, "dummy")); + var ute = assertInstanceOf(UndeclaredThrowableException.class, ite.getCause(), "unwrap the InvocationTargetException"); + assertInstanceOf(IllegalAccessException.class, ute.getCause(), "unwrap the UndeclaredThrowableException"); } /* diff --git a/test/jdk/java/lang/reflect/annotationSharing/AnnotationSharing.java b/test/jdk/java/lang/reflect/annotationSharing/AnnotationSharing.java index 98a13d6416c..b29c74637d6 100644 --- a/test/jdk/java/lang/reflect/annotationSharing/AnnotationSharing.java +++ b/test/jdk/java/lang/reflect/annotationSharing/AnnotationSharing.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -27,13 +27,13 @@ * @summary Test sharing of annotations between Executable/Field instances. * Sharing should not be noticeable when performing mutating * operations. - * @run testng AnnotationSharing + * @run junit AnnotationSharing */ import java.lang.annotation.*; import java.lang.reflect.*; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; public class AnnotationSharing { @Test diff --git a/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java b/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java index f3a429027d9..6951cc0829c 100644 --- a/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java +++ b/test/jdk/java/lang/reflect/callerCache/CustomLoaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, 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 @@ -27,7 +27,7 @@ * @library /test/lib/ * @modules jdk.compiler * @build CustomLoaderTest jdk.test.lib.compiler.CompilerUtils - * @run testng/othervm CustomLoaderTest + * @run junit/othervm CustomLoaderTest * * @summary Test method whose parameter types and return type are not visible to the caller. */ @@ -42,27 +42,23 @@ import java.nio.file.Paths; import java.util.concurrent.atomic.AtomicInteger; import jdk.test.lib.compiler.CompilerUtils; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class CustomLoaderTest { private static final Path CLASSES = Paths.get("classes"); - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { String src = System.getProperty("test.src", "."); String classpath = System.getProperty("test.classes", "."); boolean rc = CompilerUtils.compile(Paths.get(src, "ReflectTest.java"), CLASSES, "-cp", classpath); if (!rc) { - throw new RuntimeException("fail compilation"); - } - try { - Class p = Class.forName("ReflectTest$P"); - fail("should not be visible to this loader"); - } catch (ClassNotFoundException e) { - e.printStackTrace(); + fail("fail compilation"); } + assertThrows(ClassNotFoundException.class, () -> Class.forName("ReflectTest$P"), + "should not be visible to this loader"); } @Test @@ -72,17 +68,17 @@ public class CustomLoaderTest { Method m1 = loader1.findMethod(); Method m2 = loader2.findMethod(); - assertTrue(m1.getDeclaringClass() != m2.getDeclaringClass()); + assertNotSame(m1.getDeclaringClass(), m2.getDeclaringClass()); - assertTrue(m1.getDeclaringClass() == loader1.c); - assertTrue(m2.getDeclaringClass() == loader2.c); + assertSame(loader1.c, m1.getDeclaringClass()); + assertSame(loader2.c, m2.getDeclaringClass()); Object o1 = m1.invoke(loader1.c.newInstance(), loader1.p.newInstance(), loader1.q.newInstance()); Object o2 = m2.invoke(loader2.c.newInstance(), loader2.p.newInstance(), loader2.q.newInstance()); - assertTrue(o1.getClass() != o2.getClass()); - assertTrue(o1.getClass() == loader1.r); - assertTrue(o2.getClass() == loader2.r); + assertNotSame(o1.getClass(), o2.getClass()); + assertSame(loader1.r, o1.getClass()); + assertSame(loader2.r, o2.getClass()); } static class TestLoader extends URLClassLoader { diff --git a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java index b640a5e2150..4513c698a54 100644 --- a/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java +++ b/test/jdk/java/lang/reflect/callerCache/ReflectionCallerCacheTest.java @@ -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 @@ -29,7 +29,7 @@ * @library /test/lib/ * @modules jdk.compiler * @build ReflectionCallerCacheTest Members jdk.test.lib.compiler.CompilerUtils - * @run testng/othervm ReflectionCallerCacheTest + * @run junit/othervm ReflectionCallerCacheTest */ import java.io.IOException; @@ -44,16 +44,16 @@ import java.util.concurrent.Callable; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.util.ForceGC; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ReflectionCallerCacheTest { private static final Path CLASSES = Paths.get("classes"); private static final ReflectionCallerCacheTest TEST = new ReflectionCallerCacheTest(); - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { String src = System.getProperty("test.src", "."); String classpath = System.getProperty("test.classes", "."); boolean rc = CompilerUtils.compile(Paths.get(src, "AccessTest.java"), CLASSES, "-cp", classpath); @@ -61,8 +61,8 @@ public class ReflectionCallerCacheTest { throw new RuntimeException("fail compilation"); } } - @DataProvider(name = "memberAccess") - public Object[][] memberAccess() { + + public static Object[][] memberAccess() { return new Object[][] { { "AccessTest$PublicConstructor" }, { "AccessTest$PublicMethod" }, @@ -102,8 +102,9 @@ public class ReflectionCallerCacheTest { } } - @Test(dataProvider = "memberAccess") - private void load(String classname) throws Exception { + @ParameterizedTest + @MethodSource("memberAccess") + void load(String classname) throws Exception { WeakReference weakLoader = loadAndRunClass(classname); // Force garbage collection to trigger unloading of class loader diff --git a/test/jdk/java/lang/reflect/records/CheckEqualityIsBasedOnFields.java b/test/jdk/java/lang/reflect/records/CheckEqualityIsBasedOnFields.java index b5c438e4f9a..c3e4431bd84 100644 --- a/test/jdk/java/lang/reflect/records/CheckEqualityIsBasedOnFields.java +++ b/test/jdk/java/lang/reflect/records/CheckEqualityIsBasedOnFields.java @@ -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 @@ -25,17 +25,15 @@ * @test * @bug 8257598 * @summary check that Record::equals uses the fields and not the accessors for the comparison - * @run testng CheckEqualityIsBasedOnFields + * @run junit CheckEqualityIsBasedOnFields */ import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.Method; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class CheckEqualityIsBasedOnFields { public record R01(boolean x) { @@ -91,8 +89,7 @@ public class CheckEqualityIsBasedOnFields { } } - @DataProvider(name = "recordData") - public Object[][] recordTypeAndExpectedValue() { + public static Object[][] recordTypeAndExpectedValue() { return new Object[][] { new Object[] { R01.class, boolean.class, new Object[]{true, false} }, new Object[] { R02.class, byte.class, new Object[]{(byte)0, (byte)1, (byte)2, (byte)3, (byte)4, (byte)5, @@ -112,7 +109,8 @@ public class CheckEqualityIsBasedOnFields { }; } - @Test(dataProvider = "recordData") + @ParameterizedTest + @MethodSource("recordTypeAndExpectedValue") public void testEqualsDoesntUseAccessors(Class clazz, Class componentClass, Object[] expectedXValues) throws Exception { Constructor ctor; Method getter, equalsMethod; @@ -125,8 +123,8 @@ public class CheckEqualityIsBasedOnFields { System.out.println(rec1.toString()); System.out.println(rec2.toString()); assertFalse((boolean) equalsMethod.invoke(rec1, rec2)); - assertNotEquals(expectedXValues[i], expectedXValues[i + expectedXValues.length / 2]); - assertEquals(getter.invoke(rec1), getter.invoke(rec2)); + assertNotEquals(expectedXValues[i + expectedXValues.length / 2], expectedXValues[i]); + assertEquals(getter.invoke(rec2), getter.invoke(rec1)); } } } diff --git a/test/jdk/java/lang/reflect/records/IsRecordTest.java b/test/jdk/java/lang/reflect/records/IsRecordTest.java index 4888e6d506e..153329a7832 100644 --- a/test/jdk/java/lang/reflect/records/IsRecordTest.java +++ b/test/jdk/java/lang/reflect/records/IsRecordTest.java @@ -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 @@ -26,32 +26,31 @@ * @bug 8255560 * @summary Class::isRecord should check that the current class is final and not abstract * @library /test/lib - * @run testng/othervm IsRecordTest + * @run junit/othervm IsRecordTest */ import java.lang.classfile.ClassFile; import java.lang.constant.ClassDesc; -import java.lang.reflect.AccessFlag; import java.util.List; import java.util.Map; import java.lang.classfile.attribute.RecordAttribute; import java.lang.classfile.attribute.RecordComponentInfo; import jdk.test.lib.ByteCodeLoader; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; import static java.lang.System.out; import static java.lang.classfile.ClassFile.ACC_ABSTRACT; import static java.lang.classfile.ClassFile.ACC_FINAL; import static java.lang.constant.ConstantDescs.CD_int; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.*; public class IsRecordTest { - @DataProvider(name = "scenarios") - public Object[][] scenarios() { + public static Object[][] scenarios() { return new Object[][] { // isFinal, isAbstract, extendJLR, withRecAttr, expectIsRecord { false, false, false, true, false }, @@ -75,7 +74,8 @@ public class IsRecordTest { * iii) direct subclass of j.l.Record (or not), along with the presence or * absence of a record attribute. */ - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("scenarios") public void testDirectSubClass(boolean isFinal, boolean isAbstract, boolean extendsJLR, @@ -92,9 +92,8 @@ public class IsRecordTest { Class cls = ByteCodeLoader.load("C", classBytes); out.println("cls=%s, Record::isAssignable=%s, isRecord=%s" .formatted(cls, Record.class.isAssignableFrom(cls), cls.isRecord())); - assertEquals(cls.isRecord(), expectIsRecord); - var getRecordComponents = cls.getRecordComponents(); - assertTrue(expectIsRecord ? getRecordComponents != null : getRecordComponents == null); + assertEquals(expectIsRecord, cls.isRecord()); + assertEquals(expectIsRecord, cls.getRecordComponents() != null); } /** @@ -102,7 +101,8 @@ public class IsRecordTest { * along with the presence or absence of a record attribute, where the class has * a superclass whose superclass is j.l.Record. */ - @Test(dataProvider = "scenarios") + @ParameterizedTest + @MethodSource("scenarios") public void testIndirectSubClass(boolean isFinal, boolean isAbstract, boolean unused1, @@ -127,8 +127,8 @@ public class IsRecordTest { .formatted(cls, Record.class.isAssignableFrom(cls), cls.isRecord())); assertFalse(supFooCls.isRecord()); assertFalse(subFooCls.isRecord()); - assertEquals(supFooCls.getRecordComponents(), null); - assertEquals(subFooCls.getRecordComponents(), null); + assertNull(supFooCls.getRecordComponents()); + assertNull(subFooCls.getRecordComponents()); } /** Tests record-ness properties of traditionally compiled classes. */ @@ -137,23 +137,23 @@ public class IsRecordTest { out.println("\n--- testBasicRecords ---"); record EmptyRecord () { } assertTrue(EmptyRecord.class.isRecord()); - assertEquals(EmptyRecord.class.getRecordComponents().length, 0); + assertEquals(0, EmptyRecord.class.getRecordComponents().length); record FooRecord (int x) { } assertTrue(FooRecord.class.isRecord()); - assertTrue(FooRecord.class.getRecordComponents() != null); + assertNotNull(FooRecord.class.getRecordComponents()); final record FinalFooRecord (int x) { } assertTrue(FinalFooRecord.class.isRecord()); - assertTrue(FinalFooRecord.class.getRecordComponents() != null); + assertNotNull(FinalFooRecord.class.getRecordComponents()); class A { } assertFalse(A.class.isRecord()); - assertFalse(A.class.getRecordComponents() != null); + assertNull(A.class.getRecordComponents()); final class B { } assertFalse(B.class.isRecord()); - assertFalse(B.class.getRecordComponents() != null); + assertNull(B.class.getRecordComponents()); } // -- infra diff --git a/test/jdk/java/lang/reflect/records/RecordReflectionTest.java b/test/jdk/java/lang/reflect/records/RecordReflectionTest.java index 0598e88f0c1..5cc2fd4f8eb 100644 --- a/test/jdk/java/lang/reflect/records/RecordReflectionTest.java +++ b/test/jdk/java/lang/reflect/records/RecordReflectionTest.java @@ -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 @@ -27,16 +27,17 @@ * @summary reflection test for records * @build R10 * @compile RecordReflectionTest.java - * @run testng/othervm RecordReflectionTest + * @run junit/othervm RecordReflectionTest */ import java.lang.annotation.*; import java.lang.reflect.*; import java.util.List; -import org.testng.annotations.*; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -@Test public class RecordReflectionTest { class NoRecord {} @@ -78,8 +79,7 @@ public class RecordReflectionTest { R13 {} // compact constructor, will contain mandated parameters } - @DataProvider(name = "recordClasses") - public Object[][] recordClassData() { + public static Object[][] recordClassData() { return List.of(R1.class, R2.class, R3.class, @@ -96,17 +96,17 @@ public class RecordReflectionTest { ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); } - @Test(dataProvider = "recordClasses") + @ParameterizedTest + @MethodSource("recordClassData") public void testIsRecord(Class cls) { String message = cls.toGenericString(); assertTrue(cls.isRecord()); - assertTrue(cls.getSuperclass() == java.lang.Record.class); - assertTrue(cls.getRecordComponents() != null); + assertSame(Record.class, cls.getSuperclass()); + assertNotNull(cls.getRecordComponents()); assertTrue(message.contains("record"), message); } - @DataProvider(name = "notRecordClasses") - public Object[][] notRecordClasses() { + public static List> notRecordClasses() { return List.of(NoRecord.class, NoRecord[].class, Record.class, // java.lang.Record is not itself a record class @@ -116,19 +116,18 @@ public class RecordReflectionTest { int.class, int[].class, long.class, - long[].class) - .stream().map(c -> new Object[] {c}).toArray(Object[][]::new); + long[].class); } - @Test(dataProvider = "notRecordClasses") + @ParameterizedTest + @MethodSource("notRecordClasses") public void testNotARecordClass(Class cls) { assertFalse(cls.isRecord()); - assertFalse(cls.getSuperclass() == java.lang.Record.class); - assertTrue(cls.getRecordComponents() == null); + assertNotSame(Record.class, cls.getSuperclass()); + assertNull(cls.getRecordComponents()); } - @DataProvider(name = "reflectionData") - public Object[][] reflectionData() { + public static Object[][] reflectionData() { return new Object[][] { new Object[] { new R1(), 0, @@ -181,7 +180,8 @@ public class RecordReflectionTest { }; } - @Test(dataProvider = "reflectionData") + @ParameterizedTest + @MethodSource("reflectionData") public void testRecordReflection(Object recordOb, int numberOfComponents, Object[] values, @@ -192,13 +192,13 @@ public class RecordReflectionTest { Class recordClass = recordOb.getClass(); assertTrue(recordClass.isRecord()); RecordComponent[] recordComponents = recordClass.getRecordComponents(); - assertEquals(recordComponents.length, numberOfComponents); + assertEquals(numberOfComponents, recordComponents.length); int i = 0; for (RecordComponent rc : recordComponents) { - assertEquals(rc.getName(), names[i]); - assertEquals(rc.getType(), rc.getAccessor().getReturnType()); - assertEquals(rc.getAccessor().invoke(recordOb), values[i]); - assertEquals(rc.getAccessor().getGenericReturnType().toString(), signatures[i], + assertEquals(names[i], rc.getName()); + assertEquals(rc.getAccessor().getReturnType(), rc.getType()); + assertEquals(values[i], rc.getAccessor().invoke(recordOb)); + assertEquals(signatures[i], rc.getAccessor().getGenericReturnType().toString(), String.format("signature of method \"%s\" different from expected signature \"%s\"", rc.getAccessor().getGenericReturnType(), signatures[i])); i++; @@ -207,7 +207,7 @@ public class RecordReflectionTest { var constructor = recordClass.getDeclaredConstructors()[0]; i = 0; for (var p: constructor.getParameters()) { - assertEquals(p.getParameterizedType().toString(), signatures[i], + assertEquals(signatures[i], p.getParameterizedType().toString(), String.format("signature of method \"%s\" different from expected signature \"%s\"", p.getType().toString(), signatures[i])); i++; @@ -215,7 +215,7 @@ public class RecordReflectionTest { // similar as above but testing another API i = 0; for (var p : constructor.getGenericParameterTypes()) { - assertEquals(p.toString(), signatures[i], + assertEquals(signatures[i], p.toString(), String.format("signature of method \"%s\" different from expected signature \"%s\"", p.toString(), signatures[i])); i++; @@ -228,16 +228,17 @@ public class RecordReflectionTest { record AnnotatedRec(@RCA int i) {} + @Test public void testDeclAnnotationsInRecordComp() throws Throwable { Class recordClass = AnnotatedRec.class; RecordComponent rc = recordClass.getRecordComponents()[0]; Annotation[] annos = rc.getAnnotations(); - assertEquals(annos.length, 1); - assertEquals(annos[0].toString(), "@RecordReflectionTest.RCA()"); + assertEquals(1, annos.length); + assertEquals("@RecordReflectionTest.RCA()", annos[0].toString()); Field f = recordClass.getDeclaredField("i"); - assertEquals(f.getAnnotations().length, 1); - assertEquals(f.getAnnotations()[0].toString(), annos[0].toString()); + assertEquals(1, f.getAnnotations().length); + assertEquals(annos[0].toString(), f.getAnnotations()[0].toString()); } @Retention(RetentionPolicy.RUNTIME) @@ -246,30 +247,28 @@ public class RecordReflectionTest { record TypeAnnotatedRec(@TYPE_USE int i) {} + @Test public void testTypeAnnotationsInRecordComp() throws Throwable { Class recordClass = TypeAnnotatedRec.class; RecordComponent rc = recordClass.getRecordComponents()[0]; AnnotatedType at = rc.getAnnotatedType(); Annotation[] annos = at.getAnnotations(); - assertEquals(annos.length, 1); - assertEquals(annos[0].toString(), "@RecordReflectionTest.TYPE_USE()"); + assertEquals(1, annos.length); + assertEquals("@RecordReflectionTest.TYPE_USE()", annos[0].toString()); Field f = recordClass.getDeclaredField("i"); - assertEquals(f.getAnnotatedType().getAnnotations().length, 1); - assertEquals(f.getAnnotatedType().getAnnotations()[0].toString(), annos[0].toString()); + assertEquals(1, f.getAnnotatedType().getAnnotations().length); + assertEquals(annos[0].toString(), f.getAnnotatedType().getAnnotations()[0].toString()); } + @Test public void testReadOnlyFieldInRecord() throws Throwable { R2 o = new R2(1, 2); Class recordClass = R2.class; String fieldName = "i"; Field f = recordClass.getDeclaredField(fieldName); assertTrue(f.trySetAccessible()); - assertTrue(f.get(o) != null); - try { - f.set(o, null); - assertTrue(false, "should fail to set " + fieldName); - } catch (IllegalAccessException e) { - } + assertNotNull(f.get(o)); + assertThrows(IllegalAccessException.class, () -> f.set(o, null)); } } diff --git a/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java b/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java index 03caebb49e8..6e14a5d540e 100644 --- a/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java +++ b/test/jdk/java/lang/reflect/sealed_classes/SealedClassesReflectionTest.java @@ -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 @@ -26,18 +26,17 @@ * @bug 8227046 * @summary reflection test for sealed classes * @compile SealedClassesReflectionTest.java - * @run testng/othervm SealedClassesReflectionTest + * @run junit/othervm SealedClassesReflectionTest */ import java.lang.annotation.*; -import java.lang.constant.ClassDesc; import java.lang.reflect.*; import java.util.Arrays; import java.util.List; -import org.testng.annotations.*; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -@Test public class SealedClassesReflectionTest { sealed class SealedClass1 permits FinalSubclass1, NonSealedSubclass1 {} @@ -68,8 +67,7 @@ public class SealedClassesReflectionTest { sealed class SealedSubclass2 implements SealedInt3 permits NonSealedSubclass2 {} non-sealed class NonSealedSubclass2 extends SealedSubclass2 {} - @DataProvider(name = "sealedClasses") - public Object[][] sealedClassesData() { + public static List> sealedClassesData() { return List.of( SealedClass1.class, SealedClass2.class, @@ -79,19 +77,19 @@ public class SealedClassesReflectionTest { SealedInt2.class, SealedInt3.class, SealedSubInt1.class - ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); + ); } - @Test(dataProvider = "sealedClasses") + @ParameterizedTest + @MethodSource("sealedClassesData") public void testSealedClasses(Class cls) { assertTrue(cls.isSealed()); - assertTrue(!Modifier.isFinal(cls.getModifiers())); - assertTrue(cls.getPermittedSubclasses() != null); + assertFalse(Modifier.isFinal(cls.getModifiers())); + assertNotNull(cls.getPermittedSubclasses()); assertTrue(cls.getPermittedSubclasses().length > 0); } - @DataProvider(name = "notSealedClasses") - public Object[][] notSealedClassesData() { + public static List> notSealedClassesData() { return List.of( Object.class, void.class, Void.class, Void[].class, @@ -104,34 +102,34 @@ public class SealedClassesReflectionTest { double.class, double[].class, Double.class, Double[].class, boolean.class, boolean[].class, Boolean.class, Boolean[].class, String.class, String[].class - ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); + ); } - @Test(dataProvider = "notSealedClasses") + @ParameterizedTest + @MethodSource("notSealedClassesData") public void testNotSealedClasses(Class cls) { - assertTrue(!cls.isSealed()); - assertTrue(cls.getPermittedSubclasses() == null); + assertFalse(cls.isSealed()); + assertNull(cls.getPermittedSubclasses()); } - @DataProvider(name = "non_sealedClasses") - public Object[][] non_sealedClassesData() { + public static List> non_sealedClassesData() { return List.of( NonSealedSubclass1.class, NonSealedSubInt1.class, NonSealedSubclass2.class - ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); + ); } - @Test(dataProvider = "non_sealedClasses") + @ParameterizedTest + @MethodSource("non_sealedClassesData") public void testnon_sealedClasses(Class cls) { - assertTrue(!cls.isSealed()); - assertTrue(!Modifier.isFinal(cls.getModifiers())); + assertFalse(cls.isSealed()); + assertFalse(Modifier.isFinal(cls.getModifiers())); assertTrue((cls.getSuperclass() != null && cls.getSuperclass().isSealed()) || Arrays.stream(cls.getInterfaces()).anyMatch(Class::isSealed)); - assertTrue(cls.getPermittedSubclasses() == null); + assertNull(cls.getPermittedSubclasses()); } - @DataProvider(name = "reflectionData") - public Object[][] reflectionData() { + public static Object[][] reflectionData() { return new Object[][] { new Object[] { SealedClass1.class, @@ -204,7 +202,8 @@ public class SealedClassesReflectionTest { SEALED, NON_SEALED, FINAL } - @Test(dataProvider = "reflectionData") + @ParameterizedTest + @MethodSource("reflectionData") public void testSealedReflection(Class sealedClass, int numberOfSubclasses, String[] subclassDescriptors, @@ -213,10 +212,10 @@ public class SealedClassesReflectionTest { throws ReflectiveOperationException { assertTrue(sealedClass.isSealed()); - assertTrue(sealedClass.getPermittedSubclasses().length == numberOfSubclasses); + assertEquals(numberOfSubclasses, sealedClass.getPermittedSubclasses().length); int i = 0; for (Class cd : sealedClass.getPermittedSubclasses()) { - assertTrue(cd.getName().equals(subclassDescriptors[i]), "expected: " + subclassDescriptors[i] + " found: " + cd.getName()); + assertEquals(subclassDescriptors[i], cd.getName()); i++; } i = 0; From a5d0b05136e34871366441a8c8e6bda5f20c617c Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Tue, 27 Jan 2026 15:04:26 +0000 Subject: [PATCH 015/215] 8376274: JSpec preview support and output enhancement Reviewed-by: hannesw --- .../src/classes/build/tools/taglet/JSpec.java | 69 ++++++++++++++++--- .../lang/runtime/ExactConversionsSupport.java | 11 ++- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/make/jdk/src/classes/build/tools/taglet/JSpec.java b/make/jdk/src/classes/build/tools/taglet/JSpec.java index 1c301e94a8a..7e1e0ca215e 100644 --- a/make/jdk/src/classes/build/tools/taglet/JSpec.java +++ b/make/jdk/src/classes/build/tools/taglet/JSpec.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 @@ -49,13 +49,15 @@ import static com.sun.source.doctree.DocTree.Kind.*; * The tags can be used as follows: * *
- * @jls section-number description
+ * @jls chapter.section description
+ * @jls preview-feature-chapter.section description
  * 
* * For example: * *
  * @jls 3.4 Line Terminators
+ * @jls primitive-types-in-patterns-instanceof-switch-5.7.1 Exact Testing Conversions
  * 
* * will produce the following HTML, depending on the file containing @@ -64,10 +66,24 @@ import static com.sun.source.doctree.DocTree.Kind.*; *
{@code
  * 
See Java Language Specification: *
3.4 Line terminators + *
+ * 5.7.1 Exact Testing Conversions + * PREVIEW * }
* - * Copies of JLS and JVMS are expected to have been placed in the {@code specs} - * folder. These documents are not included in open-source repositories. + * In inline tags (note you need manual JLS/JVMS prefix): + *
+ * JLS {@jls 3.4}
+ * 
+ * + * produces (note the section sign and no trailing dot): + *
+ * JLS §3.4
+ * 
+ * + * Copies of JLS, JVMS, and preview JLS and JVMS changes are expected to have + * been placed in the {@code specs} folder. These documents are not included + * in open-source repositories. */ public class JSpec implements Taglet { @@ -87,9 +103,9 @@ public class JSpec implements Taglet { } } - private String tagName; - private String specTitle; - private String idPrefix; + private final String tagName; + private final String specTitle; + private final String idPrefix; JSpec(String tagName, String specTitle, String idPrefix) { this.tagName = tagName; @@ -98,7 +114,7 @@ public class JSpec implements Taglet { } // Note: Matches special cases like @jvms 6.5.checkcast - private static final Pattern TAG_PATTERN = Pattern.compile("(?s)(.+ )?(?[1-9][0-9]*)(?
[0-9a-z_.]*)( .*)?$"); + private static final Pattern TAG_PATTERN = Pattern.compile("(?s)(.+ )?(?([a-z0-9]+-)+)?(?[1-9][0-9]*)(?
[0-9a-z_.]*)( .*)?$"); /** * Returns the set of locations in which the tag may be used. @@ -157,19 +173,50 @@ public class JSpec implements Taglet { .trim(); Matcher m = TAG_PATTERN.matcher(tagText); if (m.find()) { + // preview-feature-4.6 is preview-feature-, 4, .6 + String preview = m.group("preview"); // null if no preview feature String chapter = m.group("chapter"); String section = m.group("section"); String rootParent = currentPath().replaceAll("[^/]+", ".."); - String url = String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", - rootParent, idPrefix, chapter, section); + String url = preview == null ? + String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", + rootParent, idPrefix, chapter, section) : + String.format("%1$s/specs/%5$s%2$s.html#%2$s-%3$s%4$s", + rootParent, idPrefix, chapter, section, preview); + + var literal = expand(contents).trim(); + var prefix = (preview == null ? "" : preview) + chapter + section; + if (literal.startsWith(prefix)) { + var hasFullTitle = literal.length() > prefix.length(); + if (hasFullTitle) { + // Drop the preview identifier + literal = chapter + section + literal.substring(prefix.length()); + } else { + // No section sign if the tag refers to a chapter, like {@jvms 4} + String sectionSign = section.isEmpty() ? "" : "§"; + // Change whole text to "§chapter.x" in inline tags. + literal = sectionSign + chapter + section; + } + } sb.append("") - .append(expand(contents)) + .append(literal) .append(""); + if (preview != null) { + // Add PREVIEW superscript that links to JLS/JVMS 1.5.1 + // "Restrictions on the Use of Preview Features" + // Similar to how APIs link to the Preview info box warning + var sectionLink = String.format("%1$s/specs/%2$s/%2$s-%3$s.html#%2$s-%3$s%4$s", + rootParent, idPrefix, "1", ".5.1"); + sb.append("PREVIEW"); + } + if (tag.getKind() == DocTree.Kind.UNKNOWN_BLOCK_TAG) { sb.append("
"); } diff --git a/src/java.base/share/classes/java/lang/runtime/ExactConversionsSupport.java b/src/java.base/share/classes/java/lang/runtime/ExactConversionsSupport.java index a1a93859c42..8d4389f7295 100644 --- a/src/java.base/share/classes/java/lang/runtime/ExactConversionsSupport.java +++ b/src/java.base/share/classes/java/lang/runtime/ExactConversionsSupport.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 @@ -64,12 +64,9 @@ package java.lang.runtime; * floating-point type is considered exact. * * - * @see - * JLS 5.7.1 Exact Testing Conversions - * @see - * JLS 5.7.2 Unconditionally Exact Testing Conversions - * @see - * JLS 15.20.2 The instanceof Operator + * @jls primitive-types-in-patterns-instanceof-switch-5.7.1 Exact Testing Conversions + * @jls primitive-types-in-patterns-instanceof-switch-5.7.2 Unconditionally Exact Testing Conversions + * @jls primitive-types-in-patterns-instanceof-switch-15.20.2 The {@code instanceof} Operator * * @implNote Some exactness checks describe a test which can be redirected * safely through one of the existing methods. Those are omitted too (i.e., From e8048c87bc9c152932ee59cb674bdb6670db2a56 Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Tue, 27 Jan 2026 16:07:45 +0000 Subject: [PATCH 016/215] 8376509: [process] Problemlist Test java/lang/ProcessBuilder/PipelineLeaksFD.java Reviewed-by: jpai --- test/jdk/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 291b2163b0d..5e8406b13a6 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -503,6 +503,7 @@ java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java 8151492 generic- java/lang/invoke/LFCaching/LFGarbageCollectedTest.java 8078602 generic-all java/lang/invoke/lambda/LambdaFileEncodingSerialization.java 8249079 linux-all java/lang/invoke/RicochetTest.java 8251969 generic-all +java/lang/ProcessBuilder/PipelineLeaksFD.java 8375585 generic-all ############################################################################ From eb6e74b1fa794bf16f572d5dbce157d1cae4c505 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Tue, 27 Jan 2026 17:14:40 +0000 Subject: [PATCH 017/215] 8374176: Update --release 26 symbol information for JDK 26 build 32 Reviewed-by: liach, iris, darcy --- src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt b/src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt index 29ecd9f0304..9166b505c51 100644 --- a/src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt +++ b/src/jdk.compiler/share/data/symbols/java.base-Q.sym.txt @@ -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 @@ -429,6 +429,10 @@ innerclass innerClass java/io/ObjectOutputStream$PutField outerClass java/io/Obj innerclass innerClass java/util/AbstractMap$SimpleImmutableEntry outerClass java/util/AbstractMap innerClassName SimpleImmutableEntry flags 9 innerclass innerClass java/util/concurrent/ConcurrentHashMap$CollectionView outerClass java/util/concurrent/ConcurrentHashMap innerClassName CollectionView flags 408 +class name java/util/concurrent/CopyOnWriteArraySet +header extends java/util/AbstractSet implements java/io/Serializable flags 21 signature Ljava/util/AbstractSet;Ljava/io/Serializable; +innerclass innerClass java/io/ObjectInputStream$GetField outerClass java/io/ObjectInputStream innerClassName GetField flags 409 + class name java/util/concurrent/StructuredTaskScope -method name open descriptor (Ljava/util/concurrent/StructuredTaskScope$Joiner;Ljava/util/function/Function;)Ljava/util/concurrent/StructuredTaskScope; method name open descriptor (Ljava/util/concurrent/StructuredTaskScope$Joiner;Ljava/util/function/UnaryOperator;)Ljava/util/concurrent/StructuredTaskScope; flags 9 signature (Ljava/util/concurrent/StructuredTaskScope$Joiner<-TT;+TR;>;Ljava/util/function/UnaryOperator;)Ljava/util/concurrent/StructuredTaskScope; From fa1b1d677ac492dfdd3110b9303a4c2b009046c8 Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Tue, 27 Jan 2026 20:39:35 +0000 Subject: [PATCH 018/215] 8375477: CoreUtils support for SA tests should attempt to locate and unzip core files when they have been zipped Reviewed-by: lmesnik, kevinw --- test/lib/jdk/test/lib/util/CoreUtils.java | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/lib/jdk/test/lib/util/CoreUtils.java b/test/lib/jdk/test/lib/util/CoreUtils.java index f7a486def2b..72c1d89af44 100644 --- a/test/lib/jdk/test/lib/util/CoreUtils.java +++ b/test/lib/jdk/test/lib/util/CoreUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -203,6 +203,7 @@ public class CoreUtils { private static final String CORE_PATTERN_FILE_NAME = "/proc/sys/kernel/core_pattern"; private static final String LOCATION_STRING = "location: "; + private static final String ALT_LOCATION_STRING = "alternatively, falling back to"; private static String parseCoreFileLocationFromOutput(String crashOutputString) { System.out.println("crashOutputString = [" + crashOutputString + "]"); @@ -220,12 +221,25 @@ public class CoreUtils { // Find the core file name in the output. String coreWithPid; - if (stringWithLocation.contains("or ") && !Platform.isWindows()) { - Matcher m = Pattern.compile("or.* ([^ ]+[^\\)])\\)?").matcher(stringWithLocation); + if (Platform.isLinux() && stringWithLocation.contains(ALT_LOCATION_STRING)) { + /* + * If hotspot detects that /proc/sys/kernel/core_pattern starts with a "|", + * it generates a messages something like the following: + * # Core dump will be written. Default location: Determined by the following: + * "/var/bin/core.sh %p" (alternatively, falling back to + * /ws/jdk/build/linux-x64/test-support/jtreg_open_test_hotspot_jtreg_serviceability_sa_ClhsdbFindPC_java/scratch/2/core.10353) + * We try to detect this and glean this alternative path from the message. The + * hope here is that either this is where the core file actually ends up, or that + * the script referenced by the core_pattern is just zipping the core file, in + * which case a call to unzipCores() will have already unzipped the core file + * into this path. + */ + Matcher m = Pattern.compile(ALT_LOCATION_STRING + ".* ([^ ]+[^\\)])\\)?").matcher(stringWithLocation); if (!m.find()) { throw new RuntimeException("Couldn't find path to core inside location string"); } coreWithPid = m.group(1); + System.out.println("getCoreFileLocation found alt coreWithPid = " + coreWithPid); } else { coreWithPid = stringWithLocation.trim(); } From 1161a640abe454b47de95ed73452a78535160deb Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 28 Jan 2026 06:58:50 +0000 Subject: [PATCH 019/215] 8373239: Test java/awt/print/PrinterJob/PageRanges.java fails with incorrect selection of printed pages Reviewed-by: prr, aivanov --- .../windows/classes/sun/awt/windows/WPrinterJob.java | 6 +++++- test/jdk/java/awt/print/PrinterJob/PageRanges.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java index 01be8587685..1da9db3a35b 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WPrinterJob.java @@ -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 @@ -1733,6 +1733,10 @@ public final class WPrinterJob extends RasterPrinterJob if (isRangeSet) { attributes.add(new PageRanges(from, to)); setPageRange(from, to); + } else { + attributes.remove(PageRanges.class); + setPageRange(Pageable.UNKNOWN_NUMBER_OF_PAGES, + Pageable.UNKNOWN_NUMBER_OF_PAGES); } defaultCopies = false; attributes.add(new Copies(copies)); diff --git a/test/jdk/java/awt/print/PrinterJob/PageRanges.java b/test/jdk/java/awt/print/PrinterJob/PageRanges.java index aea60516f78..691357d5a3c 100644 --- a/test/jdk/java/awt/print/PrinterJob/PageRanges.java +++ b/test/jdk/java/awt/print/PrinterJob/PageRanges.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6575331 8297191 + * @bug 6575331 8297191 8373239 * @key printer * @summary The specified pages should be printed. * @library /java/awt/regtesthelpers From 88c8a55a4337a857ac17ffff068f730f67cf5763 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 28 Jan 2026 07:44:31 +0000 Subject: [PATCH 020/215] 8373266: Strengthen constant CardTable base accesses Reviewed-by: tschatzl, xpeng --- .../cpu/aarch64/macroAssembler_aarch64.cpp | 7 +++--- .../cardTableBarrierSetAssembler_arm.cpp | 15 +++++------- .../cardTableBarrierSetAssembler_ppc.cpp | 9 ++++---- .../shenandoahBarrierSetAssembler_ppc.cpp | 3 --- .../cpu/riscv/macroAssembler_riscv.cpp | 5 ++-- .../cardTableBarrierSetAssembler_s390.cpp | 10 ++++---- .../cardTableBarrierSetAssembler_x86.cpp | 15 ++++-------- .../os_cpu/linux_arm/javaThread_linux_arm.cpp | 3 ++- src/hotspot/share/ci/ciUtilities.cpp | 9 +++----- src/hotspot/share/ci/ciUtilities.hpp | 4 ++-- src/hotspot/share/compiler/disassembler.cpp | 23 ++++++++++++++++--- .../gc/shared/c1/cardTableBarrierSetC1.cpp | 7 ++---- .../gc/shared/c2/cardTableBarrierSetC2.cpp | 2 +- .../share/gc/shared/cardTableBarrierSet.hpp | 9 ++++++++ .../share/jvmci/jvmciCompilerToVMInit.cpp | 19 ++++++++++----- 15 files changed, 75 insertions(+), 65 deletions(-) diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 27428a5c558..e813a70372b 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -5606,12 +5606,11 @@ void MacroAssembler::adrp(Register reg1, const Address &dest, uint64_t &byte_off } void MacroAssembler::load_byte_map_base(Register reg) { - CardTable::CardValue* byte_map_base = - ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); - // Strictly speaking the byte_map_base isn't an address at all, and it might + // Strictly speaking the card table base isn't an address at all, and it might // even be negative. It is thus materialised as a constant. - mov(reg, (uint64_t)byte_map_base); + mov(reg, (uint64_t)ctbs->card_table_base_const()); } void MacroAssembler::build_frame(int framesize) { diff --git a/src/hotspot/cpu/arm/gc/shared/cardTableBarrierSetAssembler_arm.cpp b/src/hotspot/cpu/arm/gc/shared/cardTableBarrierSetAssembler_arm.cpp index 2427d46cafa..5d63035ac69 100644 --- a/src/hotspot/cpu/arm/gc/shared/cardTableBarrierSetAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/gc/shared/cardTableBarrierSetAssembler_arm.cpp @@ -67,9 +67,7 @@ void CardTableBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet d void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register tmp) { BLOCK_COMMENT("CardTablePostBarrier"); - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); Label L_cardtable_loop, L_done; @@ -83,7 +81,7 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ sub(count, count, addr); // nb of cards // warning: Rthread has not been preserved - __ mov_address(tmp, (address) ct->byte_map_base()); + __ mov_address(tmp, (address)ctbs->card_table_base_const()); __ add(addr,tmp, addr); Register zero = __ zero_register(tmp); @@ -122,8 +120,7 @@ void CardTableBarrierSetAssembler::store_check_part1(MacroAssembler* masm, Regis assert(bs->kind() == BarrierSet::CardTableBarrierSet, "Wrong barrier set kind"); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); // Load card table base address. @@ -140,7 +137,7 @@ void CardTableBarrierSetAssembler::store_check_part1(MacroAssembler* masm, Regis Possible cause is a cache miss (card table base address resides in a rarely accessed area of thread descriptor). */ - __ mov_address(card_table_base, (address)ct->byte_map_base()); + __ mov_address(card_table_base, (address)ctbs->card_table_base_const()); } // The 2nd part of the store check. @@ -170,8 +167,8 @@ void CardTableBarrierSetAssembler::store_check_part2(MacroAssembler* masm, Regis void CardTableBarrierSetAssembler::set_card(MacroAssembler* masm, Register card_table_base, Address card_table_addr, Register tmp) { CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); - CardTable* ct = ctbs->card_table(); - if ((((uintptr_t)ct->byte_map_base() & 0xff) == 0)) { + + if ((((uintptr_t)ctbs->card_table_base_const() & 0xff) == 0)) { // Card table is aligned so the lowest byte of the table address base is zero. // This works only if the code is not saved for later use, possibly // in a context where the base would no longer be aligned. diff --git a/src/hotspot/cpu/ppc/gc/shared/cardTableBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shared/cardTableBarrierSetAssembler_ppc.cpp index 7404f7e2e5c..297ce57a394 100644 --- a/src/hotspot/cpu/ppc/gc/shared/cardTableBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shared/cardTableBarrierSetAssembler_ppc.cpp @@ -103,8 +103,7 @@ void CardTableBarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Registe void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register preserve) { - CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); assert_different_registers(addr, count, R0); Label Lskip_loop, Lstore_loop; @@ -117,7 +116,7 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ srdi(addr, addr, CardTable::card_shift()); __ srdi(count, count, CardTable::card_shift()); __ subf(count, addr, count); - __ add_const_optimized(addr, addr, (address)ct->byte_map_base(), R0); + __ add_const_optimized(addr, addr, (address)ctbs->card_table_base_const(), R0); __ addi(count, count, 1); __ li(R0, 0); __ mtctr(count); @@ -140,8 +139,8 @@ void CardTableBarrierSetAssembler::card_table_write(MacroAssembler* masm, } void CardTableBarrierSetAssembler::card_write_barrier_post(MacroAssembler* masm, Register store_addr, Register tmp) { - CardTableBarrierSet* bs = barrier_set_cast(BarrierSet::barrier_set()); - card_table_write(masm, bs->card_table()->byte_map_base(), tmp, store_addr); + CardTableBarrierSet* bs = CardTableBarrierSet::barrier_set(); + card_table_write(masm, bs->card_table_base_const(), tmp, store_addr); } void CardTableBarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 1f1bc7622ed..9d143c14d27 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -771,9 +771,6 @@ void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler *masm, Register b void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register preserve) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); - - ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); - CardTable* ct = bs->card_table(); assert_different_registers(addr, count, R0); Label L_skip_loop, L_store_loop; diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 43b17a13c20..fb30f64e9ed 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -5110,9 +5110,8 @@ void MacroAssembler::get_thread(Register thread) { } void MacroAssembler::load_byte_map_base(Register reg) { - CardTable::CardValue* byte_map_base = - ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base(); - mv(reg, (uint64_t)byte_map_base); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); + mv(reg, (uint64_t)ctbs->card_table_base_const()); } void MacroAssembler::build_frame(int framesize) { diff --git a/src/hotspot/cpu/s390/gc/shared/cardTableBarrierSetAssembler_s390.cpp b/src/hotspot/cpu/s390/gc/shared/cardTableBarrierSetAssembler_s390.cpp index a0da6ebe682..9bb7f63ff31 100644 --- a/src/hotspot/cpu/s390/gc/shared/cardTableBarrierSetAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/gc/shared/cardTableBarrierSetAssembler_s390.cpp @@ -83,8 +83,7 @@ void CardTableBarrierSetAssembler::resolve_jobject(MacroAssembler* masm, Registe void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, bool do_return) { - CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); NearLabel doXC, done; assert_different_registers(Z_R0, Z_R1, addr, count); @@ -105,7 +104,7 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ add2reg_with_index(count, -BytesPerHeapOop, count, addr); // Get base address of card table. - __ load_const_optimized(Z_R1, (address)ct->byte_map_base()); + __ load_const_optimized(Z_R1, (address)ctbs->card_table_base_const()); // count = (count>>shift) - (addr>>shift) __ z_srlg(addr, addr, CardTable::card_shift()); @@ -179,13 +178,12 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register store_addr, Register tmp) { // Does a store check for the oop in register obj. The content of // register obj is destroyed afterwards. - CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); assert_different_registers(store_addr, tmp); __ z_srlg(store_addr, store_addr, CardTable::card_shift()); - __ load_absolute_address(tmp, (address)ct->byte_map_base()); + __ load_absolute_address(tmp, (address)ctbs->card_table_base_const()); __ z_agr(store_addr, tmp); __ z_mvi(0, store_addr, CardTable::dirty_card_val()); } diff --git a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp index 2b91662ddb5..65e6b4e01fc 100644 --- a/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shared/cardTableBarrierSetAssembler_x86.cpp @@ -95,11 +95,7 @@ void CardTableBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet d void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, Register tmp) { - BarrierSet *bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); - intptr_t disp = (intptr_t) ct->byte_map_base(); - SHENANDOAHGC_ONLY(assert(!UseShenandoahGC, "Shenandoah byte_map_base is not constant.");) + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); Label L_loop, L_done; const Register end = count; @@ -115,7 +111,7 @@ void CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembl __ shrptr(end, CardTable::card_shift()); __ subptr(end, addr); // end --> cards count - __ mov64(tmp, disp); + __ mov64(tmp, (intptr_t)ctbs->card_table_base_const()); __ addptr(addr, tmp); __ BIND(L_loop); __ movb(Address(addr, count, Address::times_1), 0); @@ -128,10 +124,7 @@ __ BIND(L_done); void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj, Address dst) { // Does a store check for the oop in register obj. The content of // register obj is destroyed afterwards. - BarrierSet* bs = BarrierSet::barrier_set(); - - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); __ shrptr(obj, CardTable::card_shift()); @@ -142,7 +135,7 @@ void CardTableBarrierSetAssembler::store_check(MacroAssembler* masm, Register ob // So this essentially converts an address to a displacement and it will // never need to be relocated. On 64bit however the value may be too // large for a 32bit displacement. - intptr_t byte_map_base = (intptr_t)ct->byte_map_base(); + intptr_t byte_map_base = (intptr_t)ctbs->card_table_base_const(); if (__ is_simm32(byte_map_base)) { card_addr = Address(noreg, obj, Address::times_1, byte_map_base); } else { diff --git a/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp index 3dc0035ed87..d82b9d90417 100644 --- a/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp +++ b/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp @@ -43,7 +43,8 @@ void JavaThread::cache_global_variables() { BarrierSet* bs = BarrierSet::barrier_set(); if (bs->is_a(BarrierSet::CardTableBarrierSet)) { - _card_table_base = (address) (barrier_set_cast(bs)->card_table()->byte_map_base()); + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); + _card_table_base = (address)ctbs->card_table_base_const(); } else { _card_table_base = nullptr; } diff --git a/src/hotspot/share/ci/ciUtilities.cpp b/src/hotspot/share/ci/ciUtilities.cpp index 0c5b4d9824f..011fa049275 100644 --- a/src/hotspot/share/ci/ciUtilities.cpp +++ b/src/hotspot/share/ci/ciUtilities.cpp @@ -42,10 +42,7 @@ const char* basictype_to_str(BasicType t) { // ------------------------------------------------------------------ // card_table_base -CardTable::CardValue* ci_card_table_address() { - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); - assert(!UseShenandoahGC, "Shenandoah byte_map_base is not constant."); - return ct->byte_map_base(); +CardTable::CardValue* ci_card_table_address_const() { + CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); + return ctbs->card_table_base_const(); } diff --git a/src/hotspot/share/ci/ciUtilities.hpp b/src/hotspot/share/ci/ciUtilities.hpp index 75dbb03adf4..3333972d57d 100644 --- a/src/hotspot/share/ci/ciUtilities.hpp +++ b/src/hotspot/share/ci/ciUtilities.hpp @@ -51,9 +51,9 @@ inline const char* bool_to_str(bool b) { const char* basictype_to_str(BasicType t); -CardTable::CardValue* ci_card_table_address(); +CardTable::CardValue* ci_card_table_address_const(); template T ci_card_table_address_as() { - return reinterpret_cast(ci_card_table_address()); + return reinterpret_cast(ci_card_table_address_const()); } #endif // SHARE_CI_CIUTILITIES_HPP diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index c79c15e0f32..2c1ef235e07 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -607,10 +607,27 @@ void decode_env::print_address(address adr) { return; } + address card_table_base = nullptr; BarrierSet* bs = BarrierSet::barrier_set(); - if (bs->is_a(BarrierSet::CardTableBarrierSet) && - adr == ci_card_table_address_as
()) { - st->print("word_map_base"); +#if INCLUDE_G1GC + if (bs->is_a(BarrierSet::G1BarrierSet)) { + G1BarrierSet* g1bs = barrier_set_cast(bs); + card_table_base = g1bs->card_table()->byte_map_base(); + } else +#endif +#if INCLUDE_SHENANDOAHGC + if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { + ShenandoahBarrierSet* sbs = barrier_set_cast(bs); + if (sbs->card_table() != nullptr) { + card_table_base = sbs->card_table()->byte_map_base(); + } + } else +#endif + if (bs->is_a(BarrierSet::CardTableBarrierSet)) { + card_table_base = ci_card_table_address_as
(); + } + if (card_table_base != nullptr && adr == card_table_base) { + st->print("card_table_base"); if (WizardMode) st->print(" " INTPTR_FORMAT, p2i(adr)); return; } diff --git a/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp b/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp index ebc1c1c7fb7..914358760aa 100644 --- a/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shared/c1/cardTableBarrierSetC1.cpp @@ -104,11 +104,8 @@ void CardTableBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr, LIR_Op return; } - BarrierSet* bs = BarrierSet::barrier_set(); - CardTableBarrierSet* ctbs = barrier_set_cast(bs); - CardTable* ct = ctbs->card_table(); - LIR_Const* card_table_base = new LIR_Const(ct->byte_map_base()); - SHENANDOAHGC_ONLY(assert(!UseShenandoahGC, "Shenandoah byte_map_base is not constant.");) + CardTableBarrierSet* ctbs = barrier_set_cast(BarrierSet::barrier_set()); + LIR_Const* card_table_base = new LIR_Const(ctbs->card_table_base_const()); if (addr->is_address()) { LIR_Address* address = addr->as_address_ptr(); diff --git a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp index fada2672e9f..42af77ebdf4 100644 --- a/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shared/c2/cardTableBarrierSetC2.cpp @@ -116,7 +116,7 @@ Node* CardTableBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access Node* CardTableBarrierSetC2::byte_map_base_node(GraphKit* kit) const { // Get base of card map - CardTable::CardValue* card_table_base = ci_card_table_address(); + CardTable::CardValue* card_table_base = ci_card_table_address_const(); if (card_table_base != nullptr) { return kit->makecon(TypeRawPtr::make((address)card_table_base)); } else { diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp index faeb007c77d..c298bfcd0c2 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp @@ -27,6 +27,7 @@ #include "gc/shared/barrierSet.hpp" #include "gc/shared/cardTable.hpp" +#include "gc/shared/gc_globals.hpp" #include "memory/memRegion.hpp" #include "utilities/align.hpp" @@ -60,6 +61,10 @@ public: CardTableBarrierSet(CardTable* card_table); virtual ~CardTableBarrierSet(); + inline static CardTableBarrierSet* barrier_set() { + return barrier_set_cast(BarrierSet::barrier_set()); + } + template inline void write_ref_field_pre(T* addr) {} @@ -86,6 +91,10 @@ public: inline void write_ref_array(HeapWord* start, size_t count); CardTable* card_table() const { return _card_table; } + CardValue* card_table_base_const() const { + assert(UseSerialGC || UseParallelGC, "Only these GCs have constant card table base"); + return _card_table->byte_map_base(); + } virtual void on_slowpath_allocation_exit(JavaThread* thread, oop new_obj); diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 801a9bf8eb4..3e9d8c97b88 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -236,16 +236,23 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { JVMTI_ONLY( _should_notify_object_alloc = &JvmtiExport::_should_notify_object_alloc; ) BarrierSet* bs = BarrierSet::barrier_set(); +#if INCLUDE_G1GC + if (bs->is_a(BarrierSet::G1BarrierSet)) { + cardtable_start_address = nullptr; + cardtable_shift = CardTable::card_shift(); + } else +#endif +#if INCLUDE_SHENANDOAHGC + if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { + cardtable_start_address = nullptr; + cardtable_shift = CardTable::card_shift(); + } else +#endif if (bs->is_a(BarrierSet::CardTableBarrierSet)) { - CardTable::CardValue* base = ci_card_table_address(); + CardTable::CardValue* base = ci_card_table_address_const(); assert(base != nullptr, "unexpected byte_map_base"); cardtable_start_address = base; cardtable_shift = CardTable::card_shift(); -#if INCLUDE_SHENANDOAHGC - } else if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { - cardtable_start_address = nullptr; - cardtable_shift = CardTable::card_shift(); -#endif } else { // No card mark barriers cardtable_start_address = nullptr; From b2cd3b0d48bdabacfd421dee9b9f87a003e0e09d Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Wed, 28 Jan 2026 08:00:11 +0000 Subject: [PATCH 021/215] 8350330: C2: PhaseIdealLoop::add_parse_predicate() should mirror GraphKit::add_parse_predicate() Reviewed-by: chagedorn, qamai --- src/hotspot/share/opto/loopnode.cpp | 44 +++++------ src/hotspot/share/opto/loopnode.hpp | 3 + .../TestLoopNestTooManyTraps.java | 79 +++++++++++++++++++ 3 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 8dc34af9c19..fab354e3e3d 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -590,7 +590,7 @@ Node* PhaseIdealLoop::loop_nest_replace_iv(Node* iv_to_replace, Node* inner_iv, // Add a Parse Predicate with an uncommon trap on the failing/false path. Normal control will continue on the true path. void PhaseIdealLoop::add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt) { - if (!C->too_many_traps(reason)) { + if (!C->too_many_traps(sfpt->jvms()->method(), sfpt->jvms()->bci(), reason)) { ParsePredicateNode* parse_predicate = new ParsePredicateNode(inner_head->in(LoopNode::EntryControl), reason, &_igvn); register_control(parse_predicate, loop, inner_head->in(LoopNode::EntryControl)); Node* if_false = new IfFalseNode(parse_predicate); @@ -760,6 +760,24 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, Ideal return safepoint; } +void PhaseIdealLoop::add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* inner_head, SafePointNode* cloned_sfpt) { + if (ShortRunningLongLoop) { + add_parse_predicate(Deoptimization::Reason_short_running_long_loop, inner_head, outer_ilt, cloned_sfpt); + } + if (UseLoopPredicate) { + add_parse_predicate(Deoptimization::Reason_predicate, inner_head, outer_ilt, cloned_sfpt); + if (UseProfiledLoopPredicate) { + add_parse_predicate(Deoptimization::Reason_profile_predicate, inner_head, outer_ilt, cloned_sfpt); + } + } + + if (UseAutoVectorizationPredicate) { + add_parse_predicate(Deoptimization::Reason_auto_vectorization_check, inner_head, outer_ilt, cloned_sfpt); + } + + add_parse_predicate(Deoptimization::Reason_loop_limit_check, inner_head, outer_ilt, cloned_sfpt); +} + // If the loop has the shape of a counted loop but with a long // induction variable, transform the loop in a loop nest: an inner // loop that iterates for at most max int iterations with an integer @@ -1123,26 +1141,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { if (safepoint != nullptr) { SafePointNode* cloned_sfpt = old_new[safepoint->_idx]->as_SafePoint(); - if (ShortRunningLongLoop) { - add_parse_predicate(Deoptimization::Reason_short_running_long_loop, inner_head, outer_ilt, cloned_sfpt); - } - if (UseLoopPredicate) { - add_parse_predicate(Deoptimization::Reason_predicate, inner_head, outer_ilt, cloned_sfpt); - if (UseProfiledLoopPredicate) { - add_parse_predicate(Deoptimization::Reason_profile_predicate, inner_head, outer_ilt, cloned_sfpt); - } - } - - if (UseAutoVectorizationPredicate) { - // We only want to use the auto-vectorization check as a trap once per bci. And - // PhaseIdealLoop::add_parse_predicate only checks trap limits per method, so - // we do a custom check here. - if (!C->too_many_traps(cloned_sfpt->jvms()->method(), cloned_sfpt->jvms()->bci(), Deoptimization::Reason_auto_vectorization_check)) { - add_parse_predicate(Deoptimization::Reason_auto_vectorization_check, inner_head, outer_ilt, cloned_sfpt); - } - } - - add_parse_predicate(Deoptimization::Reason_loop_limit_check, inner_head, outer_ilt, cloned_sfpt); + add_parse_predicates(outer_ilt, inner_head, cloned_sfpt); } #ifndef PRODUCT @@ -1893,7 +1892,7 @@ bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* l #endif //------------------------------is_counted_loop-------------------------------- -bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_bt) { +bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv_bt) { PhaseGVN *gvn = &_igvn; Node* back_control = loop_exit_control(x, loop); @@ -3528,7 +3527,6 @@ void OuterStripMinedLoopNode::transform_to_counted_loop(PhaseIterGVN* igvn, Phas CountedLoopEndNode* cle = inner_cl->loopexit(); Node* inner_test = cle->in(1); IfNode* outer_le = outer_loop_end(); - CountedLoopEndNode* inner_cle = inner_cl->loopexit(); Node* safepoint = outer_safepoint(); fix_sunk_stores_when_back_to_counted_loop(igvn, iloop); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index ffc283ac941..24976d76a51 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1368,6 +1368,9 @@ public: #endif void add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt); SafePointNode* find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop); + + void add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* inner_head, SafePointNode* cloned_sfpt); + IdealLoopTree* insert_outer_loop(IdealLoopTree* loop, LoopNode* outer_l, Node* outer_ift); IdealLoopTree* create_outer_strip_mined_loop(Node* init_control, IdealLoopTree* loop, float cl_prob, float le_fcnt, diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java new file mode 100644 index 00000000000..502afc56548 --- /dev/null +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java @@ -0,0 +1,79 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8350330 + * @summary C2: PhaseIdealLoop::add_parse_predicate() should mirror GraphKit::add_parse_predicate() + * @library /test/lib / + * @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 + * -XX:-BackgroundCompilation -XX:-ShortRunningLongLoop -XX:-UseOnStackReplacement + * -XX:CompileOnly=*TestLoopNestTooManyTraps::test1 -XX:LoopMaxUnroll=0 + * compiler.longcountedloops.TestLoopNestTooManyTraps + */ + +package compiler.longcountedloops; + +import java.lang.reflect.Method; +import java.util.Objects; +import jdk.test.whitebox.WhiteBox; + +public class TestLoopNestTooManyTraps { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + public static void main(String[] args) throws NoSuchMethodException { + Method methodTest1 = TestLoopNestTooManyTraps.class.getDeclaredMethod("test1", int.class, long.class); + + for (int j = 0; j < 10; j++) { + System.out.println("iteration " + j); + for (int i = 0; i < 20_000; i++) { + test1(1000, 1000); + } + if (!WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 should be compiled"); + } + System.out.println("iteration 2 " + j); + try { + test1(10000, 1000); + } catch (IndexOutOfBoundsException ioobe) { + } + if (j <= 1) { + if (WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 should have deoptimized at iteration " + j); + } + } else { + if (!WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 shouldn't have deoptimized"); + } + } + } + } + + private static void test1(int stop, long length) { + for (int i = 0; i < stop; i++) { + Objects.checkIndex(i, length); + } + } +} From 4ae4ffd5a3114aa2a3832818ee30dc38d9aa2b72 Mon Sep 17 00:00:00 2001 From: Chad Rakoczy Date: Wed, 28 Jan 2026 08:08:36 +0000 Subject: [PATCH 022/215] 8374513: AArch64: Improve receiver type profiling reliability Reviewed-by: shade, aph --- .../cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 92 ++--------- .../cpu/aarch64/c1_LIRAssembler_aarch64.hpp | 5 +- .../cpu/aarch64/interp_masm_aarch64.cpp | 137 +--------------- .../cpu/aarch64/interp_masm_aarch64.hpp | 12 +- .../cpu/aarch64/macroAssembler_aarch64.cpp | 155 ++++++++++++++++++ .../cpu/aarch64/macroAssembler_aarch64.hpp | 2 + .../cpu/aarch64/templateTable_aarch64.cpp | 4 +- 7 files changed, 179 insertions(+), 228 deletions(-) diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 37a6a130e0d..c0621cbd5c2 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1218,43 +1218,11 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { __ bind(*op->stub()->continuation()); } -void LIR_Assembler::type_profile_helper(Register mdo, - ciMethodData *md, ciProfileData *data, - Register recv, Label* update_done) { +void LIR_Assembler::type_profile_helper(Register mdo, ciMethodData *md, + ciProfileData *data, Register recv) { - // Given a profile data offset, generate an Address which points to - // the corresponding slot in mdo->data(). - // Clobbers rscratch2. - auto slot_at = [=](ByteSize offset) -> Address { - return __ form_address(rscratch2, mdo, - md->byte_offset_of_slot(data, offset), - LogBytesPerWord); - }; - - for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) { - Label next_test; - // See if the receiver is receiver[n]. - __ ldr(rscratch1, slot_at(ReceiverTypeData::receiver_offset(i))); - __ cmp(recv, rscratch1); - __ br(Assembler::NE, next_test); - __ addptr(slot_at(ReceiverTypeData::receiver_count_offset(i)), - DataLayout::counter_increment); - __ b(*update_done); - __ bind(next_test); - } - - // Didn't find receiver; find next empty slot and fill it in - for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) { - Label next_test; - Address recv_addr(slot_at(ReceiverTypeData::receiver_offset(i))); - __ ldr(rscratch1, recv_addr); - __ cbnz(rscratch1, next_test); - __ str(recv, recv_addr); - __ mov(rscratch1, DataLayout::counter_increment); - __ str(rscratch1, slot_at(ReceiverTypeData::receiver_count_offset(i))); - __ b(*update_done); - __ bind(next_test); - } + int mdp_offset = md->byte_offset_of_slot(data, in_ByteSize(0)); + __ profile_receiver_type(recv, mdo, mdp_offset); } void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, Label* failure, Label* obj_is_null) { @@ -1316,14 +1284,9 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L __ b(*obj_is_null); __ bind(not_null); - Label update_done; Register recv = k_RInfo; __ load_klass(recv, obj); - type_profile_helper(mdo, md, data, recv, &update_done); - Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); - __ addptr(counter_addr, DataLayout::counter_increment); - - __ bind(update_done); + type_profile_helper(mdo, md, data, recv); } else { __ cbz(obj, *obj_is_null); } @@ -1430,13 +1393,9 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { __ b(done); __ bind(not_null); - Label update_done; Register recv = k_RInfo; __ load_klass(recv, value); - type_profile_helper(mdo, md, data, recv, &update_done); - Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); - __ addptr(counter_addr, DataLayout::counter_increment); - __ bind(update_done); + type_profile_helper(mdo, md, data, recv); } else { __ cbz(value, done); } @@ -2540,13 +2499,9 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { if (C1OptimizeVirtualCallProfiling && known_klass != nullptr) { // We know the type that will be seen at this call site; we can // statically update the MethodData* rather than needing to do - // dynamic tests on the receiver type - - // NOTE: we should probably put a lock around this search to - // avoid collisions by concurrent compilations + // dynamic tests on the receiver type. ciVirtualCallData* vc_data = (ciVirtualCallData*) data; - uint i; - for (i = 0; i < VirtualCallData::row_limit(); i++) { + for (uint i = 0; i < VirtualCallData::row_limit(); i++) { ciKlass* receiver = vc_data->receiver(i); if (known_klass->equals(receiver)) { Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); @@ -2554,36 +2509,13 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { return; } } - - // Receiver type not found in profile data; select an empty slot - - // Note that this is less efficient than it should be because it - // always does a write to the receiver part of the - // VirtualCallData rather than just the first time - for (i = 0; i < VirtualCallData::row_limit(); i++) { - ciKlass* receiver = vc_data->receiver(i); - if (receiver == nullptr) { - __ mov_metadata(rscratch1, known_klass->constant_encoding()); - Address recv_addr = - __ form_address(rscratch2, mdo, - md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)), - LogBytesPerWord); - __ str(rscratch1, recv_addr); - Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); - __ addptr(data_addr, DataLayout::counter_increment); - return; - } - } + // Receiver type is not found in profile data. + // Fall back to runtime helper to handle the rest at runtime. + __ mov_metadata(recv, known_klass->constant_encoding()); } else { __ load_klass(recv, recv); - Label update_done; - type_profile_helper(mdo, md, data, recv, &update_done); - // Receiver did not match any saved receiver and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - __ addptr(counter_addr, DataLayout::counter_increment); - - __ bind(update_done); } + type_profile_helper(mdo, md, data, recv); } else { // Static call __ addptr(counter_addr, DataLayout::counter_increment); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp index 21916a5f7dd..5af06fc6a1c 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.hpp @@ -50,9 +50,8 @@ friend class ArrayCopyStub; Address stack_slot_address(int index, uint shift, Register tmp, int adjust = 0); // Record the type of the receiver in ReceiverTypeData - void type_profile_helper(Register mdo, - ciMethodData *md, ciProfileData *data, - Register recv, Label* update_done); + void type_profile_helper(Register mdo, ciMethodData *md, + ciProfileData *data, Register recv); void add_debug_info_for_branch(address adr, CodeEmitInfo* info); void casw(Register addr, Register newval, Register cmpval); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp index 957c2aee1c1..2b506b241e0 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp @@ -240,15 +240,14 @@ void InterpreterMacroAssembler::load_resolved_klass_at_offset( // Rsub_klass: subklass // // Kills: -// r2, r5 +// r2 void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, Label& ok_is_subtype) { assert(Rsub_klass != r0, "r0 holds superklass"); assert(Rsub_klass != r2, "r2 holds 2ndary super array length"); - assert(Rsub_klass != r5, "r5 holds 2ndary super array scan ptr"); // Profile the not-null value's klass. - profile_typecheck(r2, Rsub_klass, r5); // blows r2, reloads r5 + profile_typecheck(r2, Rsub_klass); // blows r2 // Do the check. check_klass_subtype(Rsub_klass, r0, r2, ok_is_subtype); // blows r2 @@ -991,7 +990,6 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, - Register reg2, bool receiver_can_be_null) { if (ProfileInterpreter) { Label profile_continue; @@ -1009,7 +1007,7 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, } // Record the receiver type. - record_klass_in_profile(receiver, mdp, reg2); + profile_receiver_type(receiver, mdp, 0); bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. @@ -1018,131 +1016,6 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, } } -// This routine creates a state machine for updating the multi-row -// type profile at a virtual call site (or other type-sensitive bytecode). -// The machine visits each row (of receiver/count) until the receiver type -// is found, or until it runs out of rows. At the same time, it remembers -// the location of the first empty row. (An empty row records null for its -// receiver, and can be allocated for a newly-observed receiver type.) -// Because there are two degrees of freedom in the state, a simple linear -// search will not work; it must be a decision tree. Hence this helper -// function is recursive, to generate the required tree structured code. -// It's the interpreter, so we are trading off code space for speed. -// See below for example code. -void InterpreterMacroAssembler::record_klass_in_profile_helper( - Register receiver, Register mdp, - Register reg2, int start_row, - Label& done) { - if (TypeProfileWidth == 0) { - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - } else { - record_item_in_profile_helper(receiver, mdp, reg2, 0, done, TypeProfileWidth, - &VirtualCallData::receiver_offset, &VirtualCallData::receiver_count_offset); - } -} - -void InterpreterMacroAssembler::record_item_in_profile_helper(Register item, Register mdp, - Register reg2, int start_row, Label& done, int total_rows, - OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn) { - int last_row = total_rows - 1; - assert(start_row <= last_row, "must be work left to do"); - // Test this row for both the item and for null. - // Take any of three different outcomes: - // 1. found item => increment count and goto done - // 2. found null => keep looking for case 1, maybe allocate this cell - // 3. found something else => keep looking for cases 1 and 2 - // Case 3 is handled by a recursive call. - for (int row = start_row; row <= last_row; row++) { - Label next_test; - bool test_for_null_also = (row == start_row); - - // See if the item is item[n]. - int item_offset = in_bytes(item_offset_fn(row)); - test_mdp_data_at(mdp, item_offset, item, - (test_for_null_also ? reg2 : noreg), - next_test); - // (Reg2 now contains the item from the CallData.) - - // The item is item[n]. Increment count[n]. - int count_offset = in_bytes(item_count_offset_fn(row)); - increment_mdp_data_at(mdp, count_offset); - b(done); - bind(next_test); - - if (test_for_null_also) { - Label found_null; - // Failed the equality check on item[n]... Test for null. - if (start_row == last_row) { - // The only thing left to do is handle the null case. - cbz(reg2, found_null); - // Item did not match any saved item and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - b(done); - bind(found_null); - break; - } - // Since null is rare, make it be the branch-taken case. - cbz(reg2, found_null); - - // Put all the "Case 3" tests here. - record_item_in_profile_helper(item, mdp, reg2, start_row + 1, done, total_rows, - item_offset_fn, item_count_offset_fn); - - // Found a null. Keep searching for a matching item, - // but remember that this is an empty (unused) slot. - bind(found_null); - } - } - - // In the fall-through case, we found no matching item, but we - // observed the item[start_row] is null. - - // Fill in the item field and increment the count. - int item_offset = in_bytes(item_offset_fn(start_row)); - set_mdp_data_at(mdp, item_offset, item); - int count_offset = in_bytes(item_count_offset_fn(start_row)); - mov(reg2, DataLayout::counter_increment); - set_mdp_data_at(mdp, count_offset, reg2); - if (start_row > 0) { - b(done); - } -} - -// Example state machine code for three profile rows: -// // main copy of decision tree, rooted at row[1] -// if (row[0].rec == rec) { row[0].incr(); goto done; } -// if (row[0].rec != nullptr) { -// // inner copy of decision tree, rooted at row[1] -// if (row[1].rec == rec) { row[1].incr(); goto done; } -// if (row[1].rec != nullptr) { -// // degenerate decision tree, rooted at row[2] -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// if (row[2].rec != nullptr) { count.incr(); goto done; } // overflow -// row[2].init(rec); goto done; -// } else { -// // remember row[1] is empty -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// row[1].init(rec); goto done; -// } -// } else { -// // remember row[0] is empty -// if (row[1].rec == rec) { row[1].incr(); goto done; } -// if (row[2].rec == rec) { row[2].incr(); goto done; } -// row[0].init(rec); goto done; -// } -// done: - -void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, - Register mdp, Register reg2) { - assert(ProfileInterpreter, "must be profiling"); - Label done; - - record_klass_in_profile_helper(receiver, mdp, reg2, 0, done); - - bind (done); -} - void InterpreterMacroAssembler::profile_ret(Register return_bci, Register mdp) { if (ProfileInterpreter) { @@ -1200,7 +1073,7 @@ void InterpreterMacroAssembler::profile_null_seen(Register mdp) { } } -void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, Register reg2) { +void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass) { if (ProfileInterpreter) { Label profile_continue; @@ -1213,7 +1086,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); // Record the object type. - record_klass_in_profile(klass, mdp, reg2); + profile_receiver_type(klass, mdp, 0); } update_mdp_by_constant(mdp, mdp_delta); diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp index 2b230a3b73e..74d4430000d 100644 --- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp @@ -273,15 +273,6 @@ class InterpreterMacroAssembler: public MacroAssembler { Register test_value_out, Label& not_equal_continue); - void record_klass_in_profile(Register receiver, Register mdp, - Register reg2); - void record_klass_in_profile_helper(Register receiver, Register mdp, - Register reg2, int start_row, - Label& done); - void record_item_in_profile_helper(Register item, Register mdp, - Register reg2, int start_row, Label& done, int total_rows, - OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn); - void update_mdp_by_offset(Register mdp_in, int offset_of_offset); void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); void update_mdp_by_constant(Register mdp_in, int constant); @@ -295,11 +286,10 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, - Register scratch2, bool receiver_can_be_null = false); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); - void profile_typecheck(Register mdp, Register klass, Register scratch); + void profile_typecheck(Register mdp, Register klass); void profile_typecheck_failed(Register mdp); void profile_switch_default(Register mdp); void profile_switch_case(Register index_in_scratch, Register mdp, diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index e813a70372b..f8b5a6f825c 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -2118,6 +2118,161 @@ Address MacroAssembler::argument_address(RegisterOrConstant arg_slot, } } +// Handle the receiver type profile update given the "recv" klass. +// +// Normally updates the ReceiverData (RD) that starts at "mdp" + "mdp_offset". +// If there are no matching or claimable receiver entries in RD, updates +// the polymorphic counter. +// +// This code expected to run by either the interpreter or JIT-ed code, without +// extra synchronization. For safety, receiver cells are claimed atomically, which +// avoids grossly misrepresenting the profiles under concurrent updates. For speed, +// counter updates are not atomic. +// +void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_offset) { + assert_different_registers(recv, mdp, rscratch1, rscratch2); + + int base_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(0)); + int end_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(ReceiverTypeData::row_limit())); + int poly_count_offset = in_bytes(CounterData::count_offset()); + int receiver_step = in_bytes(ReceiverTypeData::receiver_offset(1)) - base_receiver_offset; + int receiver_to_count_step = in_bytes(ReceiverTypeData::receiver_count_offset(0)) - base_receiver_offset; + + // Adjust for MDP offsets. + base_receiver_offset += mdp_offset; + end_receiver_offset += mdp_offset; + poly_count_offset += mdp_offset; + +#ifdef ASSERT + // We are about to walk the MDO slots without asking for offsets. + // Check that our math hits all the right spots. + for (uint c = 0; c < ReceiverTypeData::row_limit(); c++) { + int real_recv_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_offset(c)); + int real_count_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_count_offset(c)); + int offset = base_receiver_offset + receiver_step*c; + int count_offset = offset + receiver_to_count_step; + assert(offset == real_recv_offset, "receiver slot math"); + assert(count_offset == real_count_offset, "receiver count math"); + } + int real_poly_count_offset = mdp_offset + in_bytes(CounterData::count_offset()); + assert(poly_count_offset == real_poly_count_offset, "poly counter math"); +#endif + + // Corner case: no profile table. Increment poly counter and exit. + if (ReceiverTypeData::row_limit() == 0) { + increment(Address(mdp, poly_count_offset), DataLayout::counter_increment); + return; + } + + Register offset = rscratch2; + + Label L_loop_search_receiver, L_loop_search_empty; + Label L_restart, L_found_recv, L_found_empty, L_polymorphic, L_count_update; + + // The code here recognizes three major cases: + // A. Fastest: receiver found in the table + // B. Fast: no receiver in the table, and the table is full + // C. Slow: no receiver in the table, free slots in the table + // + // The case A performance is most important, as perfectly-behaved code would end up + // there, especially with larger TypeProfileWidth. The case B performance is + // important as well, this is where bulk of code would land for normally megamorphic + // cases. The case C performance is not essential, its job is to deal with installation + // races, we optimize for code density instead. Case C needs to make sure that receiver + // rows are only claimed once. This makes sure we never overwrite a row for another + // receiver and never duplicate the receivers in the list, making profile type-accurate. + // + // It is very tempting to handle these cases in a single loop, and claim the first slot + // without checking the rest of the table. But, profiling code should tolerate free slots + // in the table, as class unloading can clear them. After such cleanup, the receiver + // we need might be _after_ the free slot. Therefore, we need to let at least full scan + // to complete, before trying to install new slots. Splitting the code in several tight + // loops also helpfully optimizes for cases A and B. + // + // This code is effectively: + // + // restart: + // // Fastest: receiver is already installed + // for (i = 0; i < receiver_count(); i++) { + // if (receiver(i) == recv) goto found_recv(i); + // } + // + // // Fast: no receiver, but profile is full + // for (i = 0; i < receiver_count(); i++) { + // if (receiver(i) == null) goto found_null(i); + // } + // goto polymorphic + // + // // Slow: try to install receiver + // found_null(i): + // CAS(&receiver(i), null, recv); + // goto restart + // + // polymorphic: + // count++; + // return + // + // found_recv(i): + // *receiver_count(i)++ + // + + bind(L_restart); + + // Fastest: receiver is already installed + mov(offset, base_receiver_offset); + bind(L_loop_search_receiver); + ldr(rscratch1, Address(mdp, offset)); + cmp(rscratch1, recv); + br(Assembler::EQ, L_found_recv); + add(offset, offset, receiver_step); + sub(rscratch1, offset, end_receiver_offset); + cbnz(rscratch1, L_loop_search_receiver); + + // Fast: no receiver, but profile is full + mov(offset, base_receiver_offset); + bind(L_loop_search_empty); + ldr(rscratch1, Address(mdp, offset)); + cbz(rscratch1, L_found_empty); + add(offset, offset, receiver_step); + sub(rscratch1, offset, end_receiver_offset); + cbnz(rscratch1, L_loop_search_empty); + b(L_polymorphic); + + // Slow: try to install receiver + bind(L_found_empty); + + // Atomically swing receiver slot: null -> recv. + // + // The update uses CAS, which clobbers rscratch1. Therefore, rscratch2 + // is used to hold the destination address. This is safe because the + // offset is no longer needed after the address is computed. + + lea(rscratch2, Address(mdp, offset)); + cmpxchg(/*addr*/ rscratch2, /*expected*/ zr, /*new*/ recv, Assembler::xword, + /*acquire*/ false, /*release*/ false, /*weak*/ true, noreg); + + // CAS success means the slot now has the receiver we want. CAS failure means + // something had claimed the slot concurrently: it can be the same receiver we want, + // or something else. Since this is a slow path, we can optimize for code density, + // and just restart the search from the beginning. + b(L_restart); + + // Counter updates: + + // Increment polymorphic counter instead of receiver slot. + bind(L_polymorphic); + mov(offset, poly_count_offset); + b(L_count_update); + + // Found a receiver, convert its slot offset to corresponding count offset. + bind(L_found_recv); + add(offset, offset, receiver_to_count_step); + + bind(L_count_update); + increment(Address(mdp, offset), DataLayout::counter_increment); +} + + void MacroAssembler::call_VM_leaf_base(address entry_point, int number_of_arguments, Label *retaddr) { diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 4baa07d7d49..7eb6cea0614 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -1122,6 +1122,8 @@ public: Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0); + void profile_receiver_type(Register recv, Register mdp, int mdp_offset); + void verify_sve_vector_length(Register tmp = rscratch1); void reinitialize_ptrue() { if (UseSVE > 0) { diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 07b469650f0..5d4f7103a84 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -3370,7 +3370,7 @@ void TemplateTable::invokevirtual_helper(Register index, __ load_klass(r0, recv); // profile this call - __ profile_virtual_call(r0, rlocals, r3); + __ profile_virtual_call(r0, rlocals); // get target Method & entry point __ lookup_virtual_method(r0, index, method); @@ -3500,7 +3500,7 @@ void TemplateTable::invokeinterface(int byte_no) { /*return_method=*/false); // profile this call - __ profile_virtual_call(r3, r13, r19); + __ profile_virtual_call(r3, r13); // Get declaring interface class from method, and itable index From 6afc0d8f39390d474ce8ba16533c30b4c7770388 Mon Sep 17 00:00:00 2001 From: Saranya Natarajan Date: Wed, 28 Jan 2026 09:38:20 +0000 Subject: [PATCH 023/215] 8366861: Phase AFTER_LOOP_OPTS printed even though the method has no loops Reviewed-by: chagedorn, dfenacci --- src/hotspot/share/opto/compile.cpp | 5 +++-- src/hotspot/share/opto/compile.hpp | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 7d99f4a7336..eaec1605108 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -1876,7 +1876,7 @@ void Compile::process_for_post_loop_opts_igvn(PhaseIterGVN& igvn) { // at least to this point, even if no loop optimizations were done. PhaseIdealLoop::verify(igvn); - if (has_loops() || _loop_opts_cnt > 0) { + if (_print_phase_loop_opts) { print_method(PHASE_AFTER_LOOP_OPTS, 2); } C->set_post_loop_opts_phase(); // no more loop opts allowed @@ -2404,7 +2404,8 @@ void Compile::Optimize() { if (failing()) return; - if (has_loops()) { + _print_phase_loop_opts = has_loops(); + if (_print_phase_loop_opts) { print_method(PHASE_BEFORE_LOOP_OPTS, 2); } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 12812424f5e..d9a8ea15724 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -355,6 +355,7 @@ class Compile : public Phase { bool _print_assembly; // True if we should dump assembly code for this compilation bool _print_inlining; // True if we should print inlining for this compilation bool _print_intrinsics; // True if we should print intrinsics for this compilation + bool _print_phase_loop_opts; // True if we should print before and after loop opts phase #ifndef PRODUCT uint _phase_counter; // Counter for the number of already printed phases uint _igv_idx; // Counter for IGV node identifiers From 127bfc9b0dd122c78e702867a88e0847ec362e68 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Wed, 28 Jan 2026 11:11:07 +0000 Subject: [PATCH 024/215] 8374926: EnableX86ECoreOpts was not enabled on some hybrid CPU Reviewed-by: vpaprotski, dholmes --- src/hotspot/cpu/x86/vm_version_x86.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 747daefd51d..0fff5b44e00 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -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 @@ -921,8 +921,9 @@ void VM_Version::get_processor_features() { // Check if processor has Intel Ecore if (FLAG_IS_DEFAULT(EnableX86ECoreOpts) && is_intel() && is_intel_server_family() && - (_model == 0x97 || _model == 0xAA || _model == 0xAC || _model == 0xAF || - _model == 0xCC || _model == 0xDD)) { + (supports_hybrid() || + _model == 0xAF /* Xeon 6 E-cores (Sierra Forest) */ || + _model == 0xDD /* Xeon 6+ E-cores (Clearwater Forest) */ )) { FLAG_SET_DEFAULT(EnableX86ECoreOpts, true); } From 2a465cb0eba6ffe397cf3ad8c1def06bf7a1e392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20H=C3=BCbner?= Date: Wed, 28 Jan 2026 13:14:51 +0000 Subject: [PATCH 025/215] 8371777: Clean up preferred address of G1's archive region Reviewed-by: stefank, jsikstro --- src/hotspot/share/cds/aotMappedHeapLoader.cpp | 2 +- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 2 +- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.cpp b/src/hotspot/share/cds/aotMappedHeapLoader.cpp index 84051cbd9e5..a8678ed757f 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.cpp @@ -619,7 +619,7 @@ bool AOTMappedHeapLoader::map_heap_region_impl(FileMapInfo* info) { aot_log_info(aot)("Preferred address to map heap data (to avoid relocation) is " INTPTR_FORMAT, p2i(requested_start)); // allocate from java heap - HeapWord* start = G1CollectedHeap::heap()->alloc_archive_region(word_size, (HeapWord*)requested_start); + HeapWord* start = G1CollectedHeap::heap()->alloc_archive_region(word_size); if (start == nullptr) { AOTMetaspace::report_loading_error("UseSharedSpaces: Unable to allocate java heap region for archive heap."); return false; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index b6c3c0b0907..8f83a653885 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -525,7 +525,7 @@ void G1CollectedHeap::iterate_regions_in_range(MemRegion range, const Func& func } } -HeapWord* G1CollectedHeap::alloc_archive_region(size_t word_size, HeapWord* preferred_addr) { +HeapWord* G1CollectedHeap::alloc_archive_region(size_t word_size) { assert(!is_init_completed(), "Expect to be called at JVM init time"); MutexLocker x(Heap_lock); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 8009df1fa6a..1f900c76851 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -736,12 +736,10 @@ public: void iterate_regions_in_range(MemRegion range, const Func& func); // Commit the required number of G1 region(s) according to the size requested - // and mark them as 'old' region(s). Preferred address is treated as a hint for - // the location of the archive space in the heap. The returned address may or may - // not be same as the preferred address. + // and mark them as 'old' region(s). // This API is only used for allocating heap space for the archived heap objects // in the CDS archive. - HeapWord* alloc_archive_region(size_t word_size, HeapWord* preferred_addr); + HeapWord* alloc_archive_region(size_t word_size); // Populate the G1BlockOffsetTable for archived regions with the given // memory range. From 8c86b1bb1054b565cf23156d89ee8925a4e32597 Mon Sep 17 00:00:00 2001 From: Roger Calnan Date: Wed, 28 Jan 2026 14:18:52 +0000 Subject: [PATCH 026/215] 8375325: add anchors to the options in the security man pages Reviewed-by: weijun, hchao --- src/java.base/share/man/keytool.md | 58 ++++++++++----------- src/java.security.jgss/windows/man/kinit.md | 20 +++---- src/java.security.jgss/windows/man/klist.md | 6 +-- src/java.security.jgss/windows/man/ktab.md | 10 ++-- src/jdk.jartool/share/man/jarsigner.md | 54 +++++++++---------- 5 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/java.base/share/man/keytool.md b/src/java.base/share/man/keytool.md index 19ba8c34912..1d70bd2f5f8 100644 --- a/src/java.base/share/man/keytool.md +++ b/src/java.base/share/man/keytool.md @@ -1,5 +1,5 @@ --- -# Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1998, 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 @@ -211,7 +211,7 @@ perform. ## Commands for Creating or Adding Data to the Keystore -`-gencert` +[`-gencert`]{#command-gencert} : The following are the available options for the `-gencert` command: - {`-rfc`}: Output in RFC (Request For Comment) style @@ -328,7 +328,7 @@ perform. > `keytool -alias e1 -certreq | keytool -alias ca2 -gencert > e1.cert` -`-genkeypair` +[`-genkeypair`]{#option-genkeypair} : The following are the available options for the `-genkeypair` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -478,7 +478,7 @@ perform. specified by `-startdate`, or the current date when `-startdate` isn't specified) for which the certificate should be considered valid. -`-genseckey` +[`-genseckey`]{#command-genseckey} : The following are the available options for the `-genseckey` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -521,7 +521,7 @@ perform. the same password that is used for the `-keystore`. The `-keypass` value must contain at least six characters. -`-importcert` +[`-importcert`]{#command-importcert} : The following are the available options for the `-importcert` command: - {`-noprompt`}: Do not prompt @@ -586,7 +586,7 @@ perform. entry, then the `keytool` command assumes that you're importing a certificate reply. -`-importpass` +[`-importpass`]{#command-importpass} : The following are the available options for the `-importpass` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -629,7 +629,7 @@ perform. ## Commands for Importing Contents from Another Keystore -`-importkeystore` +[`-importkeystore`]{#command-importkeystore} : The following are the available options for the `-importkeystore` command: - `-srckeystore` *keystore*: Source keystore name @@ -724,7 +724,7 @@ perform. ## Commands for Generating a Certificate Request -`-certreq` +[`-certreq`]{#command-certreq} : The following are the available options for the `-certreq` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -786,7 +786,7 @@ perform. ## Commands for Exporting Data -`-exportcert` +[`-exportcert`]{#command-exportcert} : The following are the available options for the `-exportcert` command: - {`-rfc`}: Output in RFC style @@ -834,7 +834,7 @@ perform. ## Commands for Displaying Data -`-list` +[`-list`]{#command-list} : The following are the available options for the `-list` command: - {`-rfc`}: Output in RFC style @@ -881,7 +881,7 @@ perform. You can't specify both `-v` and `-rfc` in the same command. Otherwise, an error is reported. -`-printcert` +[`-printcert`]{#command-printcert} : The following are the available options for the `-printcert` command: - {`-rfc`}: Output in RFC style @@ -946,7 +946,7 @@ perform. trusted certificate in the user keystore (specified by `-keystore`) or in the `cacerts` keystore (if `-trustcacerts` is specified). -`-printcertreq` +[`-printcertreq`]{#command-printcertreq} : The following are the available options for the `-printcertreq` command: - {`-file` *file*}: Input file name @@ -958,7 +958,7 @@ perform. command. The command reads the request from file. If there is no file, then the request is read from the standard input. -`-printcrl` +[`-printcrl`]{#command-printcrl} : The following are the available options for the `-printcrl` command: - {`-file crl`}: Input file name @@ -999,7 +999,7 @@ perform. ## Commands for Managing the Keystore -`-storepasswd` +[`-storepasswd`]{#command-storepasswd} : The following are the available options for the `-storepasswd` command: - \[`-new` *arg*\]: New password @@ -1029,7 +1029,7 @@ perform. integrity of the keystore contents. The new password is set by `-new` *arg* and must contain at least six characters. -`-keypasswd` +[`-keypasswd`]{#command-keypasswd} : The following are the available options for the `-keypasswd` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -1069,7 +1069,7 @@ perform. If the `-new` option isn't provided at the command line, then the user is prompted for it. -`-delete` +[`-delete`]{#command-delete} : The following are the available options for the `-delete` command: - \[`-alias` *alias*\]: Alias name of the entry to process @@ -1101,7 +1101,7 @@ perform. keystore. When not provided at the command line, the user is prompted for the `alias`. -`-changealias` +[`-changealias`]{#command-changealias} : The following are the available options for the `-changealias` command: - {`-alias` *alias*}: Alias name of the entry to process @@ -1143,7 +1143,7 @@ perform. ## Commands for Displaying Security-related Information -`-showinfo` +[`-showinfo`]{#command-showinfo} : The following are the available options for the `-showinfo` command: - {`-tls`}: Displays TLS configuration information @@ -1185,10 +1185,10 @@ environment or memory usage. For a list of possible interpreter options, enter These options can appear for all commands operating on a keystore: -`-storetype` *storetype* +[`-storetype`]{#option-storetype} *storetype* : This qualifier specifies the type of keystore to be instantiated. -`-keystore` *keystore* +[`-keystore`]{#option-keystore} *keystore* : The keystore location. If the JKS `storetype` is used and a keystore file doesn't yet exist, then @@ -1206,13 +1206,13 @@ These options can appear for all commands operating on a keystore: if the keystore isn't file-based. For example, when the keystore resides on a hardware token device. -`-cacerts` *cacerts* +[`-cacerts`]{#option-cacerts} *cacerts* : Operates on the *cacerts* keystore . This option is equivalent to `-keystore` *path\_to\_cacerts* `-storetype` *type\_of\_cacerts*. An error is reported if the `-keystore` or `-storetype` option is used with the `-cacerts` option. -`-storepass` \[`:env` \| `:file` \] *argument* +[`-storepass`]{#option-storepass} \[`:env` \| `:file` \] *argument* : The password that is used to protect the integrity of the keystore. If the modifier `env` or `file` isn't specified, then the password has the @@ -1237,22 +1237,22 @@ These options can appear for all commands operating on a keystore: a password is not specified, then the integrity of the retrieved information can't be verified and a warning is displayed. -`-providername` *name* +[`-providername`]{#option-providername} *name* : Used to identify a cryptographic service provider's name when listed in the security properties file. -`-addprovider` *name* +[`-addprovider`]{#option-addprovider} *name* : Used to add a security provider by name (such as SunPKCS11) . -`-providerclass` *class* +[`-providerclass`]{#option-providerclass} *class* : Used to specify the name of a cryptographic service provider's master class file when the service provider isn't listed in the security properties file. -`-providerpath` *list* +[`-providerpath`]{#option-providerpath} *list* : Used to specify the provider classpath. -`-providerarg` *arg* +[`-providerarg`]{#option-providerarg} *arg* : Used with the `-addprovider` or `-providerclass` option to represent an optional string input argument for the constructor of *class* name. @@ -1263,7 +1263,7 @@ These options can appear for all commands operating on a keystore: following two options, `-srcprotected` and `-destprotected`, are provided for the source keystore and the destination keystore respectively. -`-ext` {*name*{`:critical`} {`=`*value*}} +[`-ext`]{#option-ext} {*name*{`:critical`} {`=`*value*}} : Denotes an X.509 certificate extension. The option can be used in `-genkeypair` and `-gencert` to embed extensions into the generated certificate, or in `-certreq` to show what extensions are requested in the @@ -1276,7 +1276,7 @@ These options can appear for all commands operating on a keystore: `isCritical` attribute is `true`; otherwise, it is `false`. You can use `:c` in place of `:critical`. -`-conf` *file* +[`-conf`]{#option-conf} *file* : Specifies a pre-configured options file. ## Pre-configured options file diff --git a/src/java.security.jgss/windows/man/kinit.md b/src/java.security.jgss/windows/man/kinit.md index 130bf7b007a..a32d6634e74 100644 --- a/src/java.security.jgss/windows/man/kinit.md +++ b/src/java.security.jgss/windows/man/kinit.md @@ -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 @@ -79,34 +79,34 @@ will compromise your password. You can specify one of the following commands. After the command, specify the options for it. -`-A` +[`-A`]{#option-A} : Doesn't include addresses. -`-f` +[`-f`]{#option-f} : Issues a forwardable ticket. -`-p` +[`-p`]{#option-p} : Issues a proxiable ticket. -`-c` *cache\_name* +[`-c`]{#option-c} *cache\_name* : The cache name (for example, `FILE:D:\temp\mykrb5cc`). -`-l` *lifetime* +[`-l`]{#option-l} *lifetime* : Sets the lifetime of a ticket. The value can be one of "h:m[:s]", "NdNhNmNs", and "N". See the [MIT krb5 Time Duration definition]( http://web.mit.edu/kerberos/krb5-1.17/doc/basic/date_format.html#duration) for more information. -`-r` *renewable\_time* +[`-r`]{#option-r} *renewable\_time* : Sets the total lifetime that a ticket can be renewed. -`-R` +[`-R`]{#option-R} : Renews a ticket. -`-k` +[`-k`]{#option-k} : Uses keytab -`-t` *keytab\_filename* +[`-t`]{#option-t} *keytab\_filename* : The keytab name (for example, `D:\winnt\profiles\duke\krb5.keytab`). *principal* diff --git a/src/java.security.jgss/windows/man/klist.md b/src/java.security.jgss/windows/man/klist.md index 67c84e65dec..c4874f7dc67 100644 --- a/src/java.security.jgss/windows/man/klist.md +++ b/src/java.security.jgss/windows/man/klist.md @@ -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 @@ -45,7 +45,7 @@ the contents of the credentials cache or keytab using the `klist` tool. The ## Commands -`-c` +[`-c`]{#option-c} : Specifies that the credential cache is to be listed. The following are the options for credential cache entries: @@ -62,7 +62,7 @@ the contents of the credentials cache or keytab using the `klist` tool. The `-n` : If the `-a` option is specified, don't reverse resolve addresses. -`-k` +[`-k`]{#option-k} : Specifies that key tab is to be listed. List the keytab entries. The following are the options for keytab entries: diff --git a/src/java.security.jgss/windows/man/ktab.md b/src/java.security.jgss/windows/man/ktab.md index d6817ab81cf..36e09d56e3e 100644 --- a/src/java.security.jgss/windows/man/ktab.md +++ b/src/java.security.jgss/windows/man/ktab.md @@ -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 @@ -72,12 +72,12 @@ send a keytab file over a network in the clear. ## Commands and Options -`-l` \[`-e`\] \[`-t`\] +[`-l`]{#command-l} \[`-e`\] \[`-t`\] : Lists the keytab name and entries. When `-e` is specified, the encryption type for each entry is displayed. When `-t` is specified, the timestamp for each entry is displayed. -`-a` *principal\_name* \[*password*\] \[`-n` *kvno*\] \[`-s` *salt* \| `-f`\] \[`-append`\] +[`-a`]{#command-a} *principal\_name* \[*password*\] \[`-n` *kvno*\] \[`-s` *salt* \| `-f`\] \[`-append`\] : Adds new key entries to the keytab for the given principal name with an optional *password*. If a *kvno* is specified, new keys' Key Version Numbers equal to the value, otherwise, automatically incrementing the Key @@ -90,7 +90,7 @@ send a keytab file over a network in the clear. on the command line or in a script.** This tool will prompt for a password if it isn't specified. -`-d` *principal\_name* \[`-f`\] \[`-e` *etype*\] \[*kvno* \| `all`\| `old`\] +[`-d`]{#command-d} *principal\_name* \[`-f`\] \[`-e` *etype*\] \[*kvno* \| `all`\| `old`\] : Deletes key entries from the keytab for the specified principal. No changes are made to the Kerberos database. @@ -115,7 +115,7 @@ send a keytab file over a network in the clear. This option can be used with the `-l`, `-a` or `-d` commands. -`-k` *keytab name* +[`-k`]{#option-k} *keytab name* : Specifies the keytab name and path with the `FILE:` prefix. ## Examples diff --git a/src/jdk.jartool/share/man/jarsigner.md b/src/jdk.jartool/share/man/jarsigner.md index 0ecc159a037..d128b9c11ff 100644 --- a/src/jdk.jartool/share/man/jarsigner.md +++ b/src/jdk.jartool/share/man/jarsigner.md @@ -483,7 +483,7 @@ the following standards: `-keystore` option is relevant for signing and verifying a JAR file. In addition, aliases are specified when signing and verifying a JAR file. -`-keystore` *url* +[`-keystore`]{#option-keystore} *url* : Specifies the URL that tells the keystore location. This defaults to the file `.keystore` in the user's home directory, as determined by the `user.home` system property. @@ -517,7 +517,7 @@ the following standards: > `keytool -keystore NONE -storetype PKCS11 -list` -`-storepass` \[`:env` \| `:file`\] *argument* +[`-storepass`]{#option-storepass} \[`:env` \| `:file`\] *argument* : Specifies the password that is required to access the keystore. This is only needed when signing (not verifying) a JAR file. In that case, if a `-storepass` option isn't provided at the command line, then the user is @@ -536,7 +536,7 @@ the following standards: The password shouldn't be specified on the command line or in a script unless it is for testing purposes, or you are on a secure system. -`-storetype` *storetype* +[`-storetype`]{#option-storetype} *storetype* : Specifies the type of keystore to be instantiated. The default keystore type is the one that is specified as the value of the `keystore.type` property in the security properties file, which is returned by the static @@ -548,7 +548,7 @@ the following standards: (such as a dedicated PIN-pad or a biometric reader), then the `-protected` option must be specified and no password options can be specified. -`-keypass` \[`:env` \| `:file`\] *argument* `-certchain` *file* +[`-keypass`]{#option-keypass} \[`:env` \| `:file`\] *argument* `-certchain` *file* : Specifies the password used to protect the private key of the keystore entry addressed by the alias specified on the command line. The password is required when using `jarsigner` to sign a JAR file. If no password is @@ -568,7 +568,7 @@ the following standards: The password shouldn't be specified on the command line or in a script unless it is for testing purposes, or you are on a secure system. -`-certchain` *file* +[`-certchain`]{#option-certchain} *file* : Specifies the certificate chain to be used when the certificate chain associated with the private key of the keystore entry that is addressed by the alias specified on the command line isn't complete. This can happen @@ -579,7 +579,7 @@ the following standards: (also known as Base64 encoding) as defined by [Internet RFC 1421 Certificate Encoding Standard](http://tools.ietf.org/html/rfc1421). -`-sigfile` *file* +[`-sigfile`]{#option-sigfile} *file* : Specifies the base file name to be used for the generated `.SF` and signature block files. For example, if file is `DUKESIGN`, then the generated `.SF` and signature block files are named `DUKESIGN.SF` and `DUKESIGN.RSA`, and placed in the @@ -598,10 +598,10 @@ the following standards: file name, then each such character is converted to an underscore (\_) character to form the file name. -`-signedjar` *file* +[`-signedjar`]{#option-signedjar} *file* : Specifies the name of signed JAR file. -`-digestalg` *algorithm* +[`-digestalg`]{#option-digestalg} *algorithm* : Specifies the name of the message digest algorithm to use when digesting the entries of a JAR file. @@ -613,7 +613,7 @@ the following standards: specified algorithm or the user must specify one with the `-addprovider` or `-providerClass` options; otherwise, the command will not succeed. -`-sigalg` *algorithm* +[`-sigalg`]{#option-sigalg} *algorithm* : Specifies the name of the signature algorithm to use to sign the JAR file. This algorithm must be compatible with the private key used to sign the @@ -627,10 +627,10 @@ the following standards: For a list of standard signature algorithm names, see the Java Security Standard Algorithm Names Specification. -`-verify` +[`-verify`]{#option-verify} : Verifies a signed JAR file. -`-verbose`\[`:`*suboptions*\] +[`-verbose`]{#option-verbose}\[`:`*suboptions*\] : When the `-verbose` option appears on the command line, it indicates that the `jarsigner` use the verbose mode when signing or verifying with the suboptions determining how much information is shown. This causes the , @@ -654,7 +654,7 @@ the following standards: more)*. See [Example of Verifying a Signed JAR File] and [Example of Verification with Certificate Information]. -`-certs` +[`-certs`]{#option-certs} : If the `-certs` option appears on the command line with the `-verify` and `-verbose` options, then the output includes certificate information for each signer of the JAR file. This information includes the name of the type @@ -669,14 +669,14 @@ the following standards: the alias name for the keystore entry for that signer is displayed in parentheses. -`-revCheck` +[`-revCheck`]{#option-revCheck} : This option enables revocation checking of certificates when signing or verifying a JAR file. The `jarsigner` command attempts to make network connections to fetch OCSP responses and CRLs if the `-revCheck` option is specified on the command line. Note that revocation checks are not enabled unless this option is specified. -`-tsa` *url* +[`-tsa`]{#option-tsa} *url* : If `-tsa http://example.tsa.url` appears on the command line when signing a JAR file then a time stamp is generated for the signature. The URL, `http://example.tsa.url`, identifies the location of the Time Stamping @@ -689,7 +689,7 @@ the following standards: stamp token returned by the TSA is stored with the signature in the signature block file. -`-tsacert` *alias* +[`-tsacert`]{#option-tsacert} *alias* : When `-tsacert` *alias* appears on the command line when signing a JAR file, a time stamp is generated for the signature. The alias identifies the TSA public key certificate in the keystore that is in effect. The entry's @@ -699,7 +699,7 @@ the following standards: The TSA public key certificate must be present in the keystore when using the `-tsacert` option. -`-tsapolicyid` *policyid* +[`-tsapolicyid`]{#option-tsapolicyid} *policyid* : Specifies the object identifier (OID) that identifies the policy ID to be sent to the TSA server. If this option isn't specified, no policy ID is sent and the TSA server will choose a default policy ID. @@ -708,7 +708,7 @@ the following standards: Standardization Sector (ITU-T) standard. These identifiers are typically period-separated sets of non-negative digits like `1.2.3.4`, for example. -`-tsadigestalg` *algorithm* +[`-tsadigestalg`]{#option-tsadigestalg} *algorithm* : Specifies the message digest algorithm that is used to generate the message imprint to be sent to the TSA server. If this option isn't specified, SHA-384 will be used. @@ -718,7 +718,7 @@ the following standards: For a list of standard message digest algorithm names, see the Java Security Standard Algorithm Names Specification. -`-internalsf` +[`-internalsf`]{#option-internalsf} : In the past, the signature block file generated when a JAR file was signed included a complete encoded copy of the `.SF` file (signature file) also generated. This behavior has been changed. To reduce the overall @@ -728,7 +728,7 @@ the following standards: In practice, don't use the `-internalsf` option because it incurs higher overhead. -`-sectionsonly` +[`-sectionsonly`]{#option-sectionsonly} : If the `-sectionsonly` option appears on the command line, then the `.SF` file (signature file) generated when a JAR file is signed doesn't include a header that contains a hash of the whole manifest file. It contains only @@ -747,12 +747,12 @@ the following standards: The `-sectionsonly` option is primarily used for testing. It shouldn't be used other than for testing because using it incurs higher overhead. -`-protected` +[`-protected`]{#option-protected} : Values can be either `true` or `false`. Specify `true` when a password must be specified through a protected authentication path such as a dedicated PIN reader. -`-providerName` *providerName* +[`-providerName`]{#option-providerName} *providerName* : If more than one provider was configured in the `java.security` security properties file, then you can use the `-providerName` option to target a specific provider instance. The argument to this option is the name of the @@ -768,7 +768,7 @@ the following standards: > `jarsigner -keystore NONE -storetype PKCS11 -providerName SunPKCS11-SmartCard -list` -`-addprovider` *name* \[`-providerArg` *arg*\] +[`-addprovider`]{#option-addprovider} *name* \[`-providerArg` *arg*\] : Adds a security provider by name (such as SunPKCS11) and an optional configure argument. The value of the security provider is the name of a security provider that is defined in a module. @@ -782,7 +782,7 @@ the following standards: > `jarsigner -keystore NONE -storetype PKCS11 -addprovider SunPKCS11 -providerArg /mydir1/mydir2/token.config` -`-providerClass` *provider-class-name* \[`-providerArg` *arg*\] +[`-providerClass`]{#option-providerClass} *provider-class-name* \[`-providerArg` *arg*\] : Used to specify the name of cryptographic service provider's master class file when the service provider isn't listed in the `java.security` security properties file. Adds a security provider by fully-qualified class name and @@ -792,7 +792,7 @@ the following standards: The preferred way to load PKCS11 is by using modules. See `-addprovider`. -`-providerPath` *classpath* +[`-providerPath`]{#option-providerPath} *classpath* : Used to specify the classpath for providers specified by the `-providerClass` option. Multiple paths should be separated by the system-dependent path-separator character. @@ -804,13 +804,13 @@ the following standards: execution environment or memory usage. For a list of possible interpreter options, type `java -h` or `java -X` at the command line. -`-strict` +[`-strict`]{#option-strict} : During the signing or verifying process, the command may issue warning messages. If you specify this option, the exit code of the tool reflects the severe warning messages that this command found. See [Errors and Warnings]. -`-conf` *url* +[`-conf`]{#option-conf} *url* : Specifies a pre-configured options file. Read the [keytool documentation](keytool.html#pre-configured-options-file) for details. The property keys supported are "jarsigner.all" for all actions, @@ -818,7 +818,7 @@ the following standards: `jarsigner` arguments including the JAR file name and alias name(s) cannot be set in this file. -`-version` +[`-version`]{#option-version} : Prints the program version. ## Errors and Warnings From 8095e33ee88759cf2fbe61e2284d95f6b7fb9a3a Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 28 Jan 2026 15:02:21 +0000 Subject: [PATCH 027/215] 8375433: jar should validate automatic module names Reviewed-by: jvernee --- .../classes/sun/tools/jar/Validator.java | 35 +++++++++++- .../sun/tools/jar/resources/jar.properties | 4 ++ test/jdk/tools/jar/ValidatorTest.java | 53 ++++++++++++++++--- 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java b/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java index be694eeb1bc..c6f6f012543 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java +++ b/src/jdk.jartool/share/classes/sun/tools/jar/Validator.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 @@ -47,6 +47,8 @@ import java.util.function.Function; import java.util.function.IntSupplier; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.jar.Attributes; +import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; @@ -100,6 +102,7 @@ final class Validator { this.zf = zf; this.zis = zis; checkModuleDescriptor(MODULE_INFO); + checkAutomaticModuleName(); } static boolean validate(Main main, File zipFile) throws IOException { @@ -453,6 +456,36 @@ final class Validator { }); } + /** + * Checks whether an Automatic-Module-Name entry is valid + * and also verifies it to the name given by a compiled + * module descriptor. + */ + private void checkAutomaticModuleName() { + var entry = zf.getEntry("META-INF/MANIFEST.MF"); + if (entry == null) { + return; + } + try (InputStream jis = zf.getInputStream(entry)) { + Attributes attributes = new Manifest(jis).getMainAttributes(); + String automaticModuleName = attributes.getValue("Automatic-Module-Name"); + if (automaticModuleName == null) { + return; + } + try { + ModuleDescriptor.newAutomaticModule(automaticModuleName); + } catch (IllegalArgumentException e) { + errorAndInvalid(formatMsg("error.validator.manifest.invalid.automatic.module.name", automaticModuleName)); + } + if (md == null || automaticModuleName.equals(md.name())) { + return; + } + errorAndInvalid(formatMsg("error.validator.manifest.inconsistent.automatic.module.name", automaticModuleName, md.name())); + } catch (IOException e) { + errorAndInvalid(e.getMessage()); + } + } + /* * Checks whether or not the given versioned module descriptor's attributes * are valid when compared against the root/base module descriptor. diff --git a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties index c80377e20c1..95c5f80197d 100644 --- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties +++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties @@ -138,6 +138,10 @@ error.validator.metainf.wrong.position=\ expected entry META-INF/ to be at position 0, but found: {0} error.validator.manifest.wrong.position=\ expected entry META-INF/MANIFEST.MF to be at position 0 or 1, but found it at position: {0} +error.validator.manifest.invalid.automatic.module.name=\ + invalid module name of Automatic-Module-Name entry in manifest: {0} +error.validator.manifest.inconsistent.automatic.module.name=\ + expected Automatic-Module-Name entry in manifest: {0} to match name of compiled module: {1} warn.validator.identical.entry=\ Warning: entry {0} contains a class that\n\ is identical to an entry already in the jar diff --git a/test/jdk/tools/jar/ValidatorTest.java b/test/jdk/tools/jar/ValidatorTest.java index c2dfeae8dec..e3f649a04a2 100644 --- a/test/jdk/tools/jar/ValidatorTest.java +++ b/test/jdk/tools/jar/ValidatorTest.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 @@ -23,12 +23,8 @@ /* * @test - * @bug 8345431 + * @bug 8345431 8375433 * @summary test validator to report malformed jar file - * @library /test/lib - * @modules jdk.jartool - * @build jdk.test.lib.Platform - * jdk.test.lib.util.FileUtils * @run junit/othervm ValidatorTest */ @@ -40,6 +36,7 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertLinesMatch; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -59,13 +56,15 @@ import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import jdk.test.lib.util.FileUtils; - class ValidatorTest { private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") .orElseThrow(() -> new RuntimeException("jar tool not found") ); + private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac") + .orElseThrow(() -> + new RuntimeException("javac tool not found") + ); private final String nl = System.lineSeparator(); private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -310,6 +309,44 @@ class ValidatorTest { } } + @Test + public void testInvalidAutomaticModuleName() throws Exception { + System.out.printf("%n%n*****Creating Jar with invalid Automatic-Module-Name in Manifest*****%n%n"); + var file = Path.of("InvalidAutomaticModuleName.jar"); + var manifest = Path.of("MANIFEST.MF"); + Files.writeString(manifest, + """ + Automatic-Module-Name: default + """); + jar("--create --file " + file + " --manifest " + manifest); + var e = assertThrows(IOException.class, () -> jar("--validate --file " + file.toString())); + var err = e.getMessage(); + System.out.println(err); + assertTrue(err.contains("invalid module name of Automatic-Module-Name entry in manifest: default"), "missing warning for: default"); + } + + @Test + public void testWrongAutomaticModuleName() throws Exception { + System.out.printf("%n%n*****Creating Jar with wrong Automatic-Module-Name in Manifest*****%n%n"); + var file = Path.of("WrongAutomaticModuleName.jar"); + var foo = Path.of("module-info.java"); + Files.writeString(foo, + """ + module foo {} + """); + var manifest = Path.of("MANIFEST.MF"); + Files.writeString(manifest, + """ + Automatic-Module-Name: bar + """); + JAVAC_TOOL.run(System.out, System.err, foo.toString()); + jar("--create --file " + file + " --manifest " + manifest + " module-info.class"); + var e = assertThrows(IOException.class, () -> jar("--validate --file " + file.toString())); + var err = e.getMessage(); + System.out.println(err); + assertTrue(err.contains("expected Automatic-Module-Name entry in manifest: bar to match name of compiled module: foo"), "missing warning for: foo/bar"); + } + /** * Validates that base manifest-related entries are at expected LOC positions. *

From 0e2e66be2423335002a53d887df35d2348a3ec9f Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Wed, 28 Jan 2026 16:30:34 +0000 Subject: [PATCH 028/215] 8376402: Dependencies::print_statistics() and AbstractClassHierarchyWalker::print_statistics() are not called from PRODUCT code Reviewed-by: azafari, chagedorn --- src/hotspot/share/code/dependencies.cpp | 6 ++++-- src/hotspot/share/code/dependencies.hpp | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp index d90695739a1..dbfe1cd884e 100644 --- a/src/hotspot/share/code/dependencies.cpp +++ b/src/hotspot/share/code/dependencies.cpp @@ -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 @@ -1124,7 +1124,7 @@ class AbstractClassHierarchyWalker { Klass* find_witness(InstanceKlass* context_type, KlassDepChange* changes = nullptr); static void init(); - static void print_statistics(); + NOT_PRODUCT(static void print_statistics();) }; PerfCounter* AbstractClassHierarchyWalker::_perf_find_witness_anywhere_calls_count = nullptr; @@ -2277,6 +2277,7 @@ bool KlassDepChange::involves_context(Klass* k) { return is_contained; } +#ifndef PRODUCT void Dependencies::print_statistics() { AbstractClassHierarchyWalker::print_statistics(); } @@ -2302,6 +2303,7 @@ void AbstractClassHierarchyWalker::print_statistics() { } } } +#endif CallSiteDepChange::CallSiteDepChange(Handle call_site, Handle method_handle) : _call_site(call_site), diff --git a/src/hotspot/share/code/dependencies.hpp b/src/hotspot/share/code/dependencies.hpp index d11c51a66dc..582a08183f9 100644 --- a/src/hotspot/share/code/dependencies.hpp +++ b/src/hotspot/share/code/dependencies.hpp @@ -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 @@ -649,7 +649,7 @@ class Dependencies: public ResourceObj { }; friend class Dependencies::DepStream; - static void print_statistics(); + NOT_PRODUCT(static void print_statistics();) }; From 50d872ad7ac5fa5a3406517eb53d8f61f81706df Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Wed, 28 Jan 2026 16:30:56 +0000 Subject: [PATCH 029/215] 8376419: (fs) Minor improvement of java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java Reviewed-by: jpai --- .../UserDefinedFileAttributeView/Basic.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/test/jdk/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java b/test/jdk/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java index 4586c33521f..b729438812f 100644 --- a/test/jdk/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java +++ b/test/jdk/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.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 @@ -24,9 +24,12 @@ /* @test * @bug 4313887 6838333 8273922 * @summary Unit test for java.nio.file.attribute.UserDefinedFileAttributeView + * (use -Dseed=X to set PRNG seed) * @library ../.. /test/lib * @key randomness * @build jdk.test.lib.Platform + * @build jdk.test.lib.RandomFactory + * @build jtreg.SkippedException * @run main Basic */ @@ -38,17 +41,20 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; -import static java.nio.file.LinkOption.*; -import java.nio.file.attribute.*; +import java.nio.file.attribute.UserDefinedFileAttributeView; import java.util.Arrays; import java.util.Map; import java.util.Random; + import jdk.test.lib.Platform; +import jdk.test.lib.RandomFactory; + +import jtreg.SkippedException; public class Basic { // Must be indeterministic - private static final Random rand = new Random(); + private static final Random rand = RandomFactory.getRandom(); private static final String ATTR_NAME = "mime_type"; private static final String ATTR_VALUE = "text/plain"; @@ -281,10 +287,8 @@ public class Basic { // create temporary directory to run tests Path dir = TestUtil.createTemporaryDirectory(); try { - if (!Files.getFileStore(dir).supportsFileAttributeView("user")) { - System.out.println("UserDefinedFileAttributeView not supported - skip test"); - return; - } + if (!Files.getFileStore(dir).supportsFileAttributeView("user")) + throw new SkippedException("UserDefinedFileAttributeView not supported"); // test access to user defined attributes of regular file Path file = dir.resolve("foo.html"); @@ -310,7 +314,7 @@ public class Basic { Path link = dir.resolve("link"); Files.createSymbolicLink(link, target); try { - test(link, NOFOLLOW_LINKS); + test(link, LinkOption.NOFOLLOW_LINKS); } catch (IOException x) { // access to attributes of sym link may not be supported } finally { From 89a18c0108e10dc4ca4a4fa9e8718d49036f8871 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 28 Jan 2026 17:58:15 +0000 Subject: [PATCH 030/215] 8376432: Remove AppContext from sun/swing/DefaultLookup.java Reviewed-by: psadhukhan, azvegint, aivanov --- .../classes/sun/swing/DefaultLookup.java | 51 +++++-------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/src/java.desktop/share/classes/sun/swing/DefaultLookup.java b/src/java.desktop/share/classes/sun/swing/DefaultLookup.java index d7a7f22eba3..a2ae0921507 100644 --- a/src/java.desktop/share/classes/sun/swing/DefaultLookup.java +++ b/src/java.desktop/share/classes/sun/swing/DefaultLookup.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 @@ -29,7 +29,6 @@ import java.awt.Insets; import javax.swing.*; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; -import sun.awt.AppContext; /** * DefaultLookup provides a way to customize the lookup done by the @@ -44,17 +43,9 @@ import sun.awt.AppContext; * @author Scott Violet */ public class DefaultLookup { + /** - * Key used to store DefaultLookup for AppContext. - */ - private static final Object DEFAULT_LOOKUP_KEY = new - StringBuffer("DefaultLookup"); - /** - * Thread that last asked for a default. - */ - private static Thread currentDefaultThread; - /** - * DefaultLookup for last thread. + * DefaultLookup currently set. */ private static DefaultLookup currentDefaultLookup; @@ -63,28 +54,24 @@ public class DefaultLookup { */ private static boolean isLookupSet; - /** - * Sets the DefaultLookup instance to use for the current - * AppContext. Null implies the UIManager should be - * used. + * Sets the DefaultLookup instance to use. + * Null implies the UIManager should be used. */ public static void setDefaultLookup(DefaultLookup lookup) { synchronized(DefaultLookup.class) { if (!isLookupSet && lookup == null) { - // Null was passed in, and no one has invoked setDefaultLookup + // Null was passed in, and no one has previously invoked setDefaultLookup // with a non-null value, we don't need to do anything. return; } else if (lookup == null) { - // null was passed in, but someone has invoked setDefaultLookup + // null was passed in, but someone has previously invoked setDefaultLookup // with a non-null value, use an instance of DefaultLookup // which will fallback to UIManager. lookup = new DefaultLookup(); } isLookupSet = true; - AppContext.getAppContext().put(DEFAULT_LOOKUP_KEY, lookup); - currentDefaultThread = Thread.currentThread(); currentDefaultLookup = lookup; } } @@ -98,27 +85,13 @@ public class DefaultLookup { // No one has set a valid DefaultLookup, use UIManager. return UIManager.get(key, c.getLocale()); } - Thread thisThread = Thread.currentThread(); DefaultLookup lookup; synchronized(DefaultLookup.class) { - // See if we've already cached the DefaultLookup for this thread, - // and use it if we have. - if (thisThread == currentDefaultThread) { - // It is cached, use it. - lookup = currentDefaultLookup; - } - else { - // Not cached, get the DefaultLookup to use from the AppContext - lookup = (DefaultLookup)AppContext.getAppContext().get( - DEFAULT_LOOKUP_KEY); - if (lookup == null) { - // Fallback to DefaultLookup, which will redirect to the - // UIManager. - lookup = new DefaultLookup(); - AppContext.getAppContext().put(DEFAULT_LOOKUP_KEY, lookup); - } - // Cache the values to make the next lookup easier. - currentDefaultThread = thisThread; + lookup = currentDefaultLookup; + if (lookup == null) { + // Fallback to DefaultLookup, which will redirect to the + // UIManager. + lookup = new DefaultLookup(); currentDefaultLookup = lookup; } } From 7efa3168b706c1d061c4ee65574427ef1f50fc7b Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 28 Jan 2026 18:01:10 +0000 Subject: [PATCH 031/215] 8376434: Remove AppContext from awt ImageFetcher implementation Reviewed-by: azvegint, aivanov --- .../classes/sun/awt/image/ImageFetcher.java | 61 ++++++------------- 1 file changed, 18 insertions(+), 43 deletions(-) diff --git a/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java b/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java index 74c7c4d2ce2..917378389a4 100644 --- a/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java +++ b/src/java.desktop/share/classes/sun/awt/image/ImageFetcher.java @@ -26,7 +26,6 @@ package sun.awt.image; import java.util.Vector; -import sun.awt.AppContext; /** * An ImageFetcher is a thread used to fetch ImageFetchable objects. @@ -34,10 +33,6 @@ import sun.awt.AppContext; * thread may also be used to animate it if necessary, via the * startingAnimation() / stoppingAnimation() methods. * - * There can be up to FetcherInfo.MAX_NUM_FETCHERS_PER_APPCONTEXT - * ImageFetcher threads for each AppContext. A per-AppContext queue - * of ImageFetchables is used to track objects to fetch. - * * @author Jim Graham * @author Fred Ecks */ @@ -153,7 +148,6 @@ class ImageFetcher extends Thread { info.numWaiting++; info.waitList.wait(end - now); } catch (InterruptedException e) { - // A normal occurrence as an AppContext is disposed return null; } finally { info.numWaiting--; @@ -280,28 +274,20 @@ class ImageFetcher extends Thread { // We need to instantiate a new ImageFetcher thread. // First, figure out which ThreadGroup we'll put the // new ImageFetcher into - final AppContext appContext = AppContext.getAppContext(); - ThreadGroup threadGroup = appContext.getThreadGroup(); - ThreadGroup fetcherThreadGroup; - if (threadGroup.getParent() != null) { - // threadGroup is not the root, so we proceed - fetcherThreadGroup = threadGroup; - } else { - // threadGroup is the root ("system") ThreadGroup. - // We instead want to use its child: the "main" - // ThreadGroup. Thus, we start with the current - // ThreadGroup, and go up the tree until - // threadGroup.getParent().getParent() == null. - threadGroup = Thread.currentThread().getThreadGroup(); - ThreadGroup parent = threadGroup.getParent(); - while ((parent != null) - && (parent.getParent() != null)) { - threadGroup = parent; - parent = threadGroup.getParent(); - } - fetcherThreadGroup = threadGroup; + // We don't want the root ("system") ThreadGroup. + // We instead want to use its child: the "main" + // ThreadGroup. Thus, we start with the current + // ThreadGroup, and go up the tree until + // threadGroup.getParent().getParent() == null. + ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); + ThreadGroup parent = threadGroup.getParent(); + while ((parent != null) + && (parent.getParent() != null)) { + threadGroup = parent; + parent = threadGroup.getParent(); } - final ThreadGroup fetcherGroup = fetcherThreadGroup; + final ThreadGroup fetcherGroup = threadGroup; + for (int i = 0; i < info.fetchers.length; i++) { if (info.fetchers[i] == null) { @@ -320,12 +306,13 @@ class ImageFetcher extends Thread { } /** - * The FetcherInfo class encapsulates the per-AppContext ImageFetcher + * The FetcherInfo class encapsulates the ImageFetcher * information. This includes the array of ImageFetchers, as well as * the queue of ImageFetchable objects. */ class FetcherInfo { - static final int MAX_NUM_FETCHERS_PER_APPCONTEXT = 4; + static final int MAX_NUM_FETCHERS = 4; + static final FetcherInfo FETCHER_INFO = new FetcherInfo(); Thread[] fetchers; int numFetchers; @@ -333,25 +320,13 @@ class FetcherInfo { Vector waitList; private FetcherInfo() { - fetchers = new Thread[MAX_NUM_FETCHERS_PER_APPCONTEXT]; + fetchers = new Thread[MAX_NUM_FETCHERS]; numFetchers = 0; numWaiting = 0; waitList = new Vector<>(); } - /* The key to put()/get() the FetcherInfo into/from the AppContext. */ - private static final Object FETCHER_INFO_KEY = - new StringBuffer("FetcherInfo"); - static FetcherInfo getFetcherInfo() { - AppContext appContext = AppContext.getAppContext(); - synchronized(appContext) { - FetcherInfo info = (FetcherInfo)appContext.get(FETCHER_INFO_KEY); - if (info == null) { - info = new FetcherInfo(); - appContext.put(FETCHER_INFO_KEY, info); - } - return info; - } + return FETCHER_INFO; } } From 0722ae926ff1327c47a922b1ca0b493a0d06526e Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 28 Jan 2026 19:53:41 +0000 Subject: [PATCH 032/215] 8376433: Remove AppContext from Swing Windows L&F implementation Reviewed-by: serb, aivanov --- .../plaf/windows/AnimationController.java | 19 ++++++------------- .../swing/plaf/windows/WindowsButtonUI.java | 15 +++------------ .../swing/plaf/windows/WindowsCheckBoxUI.java | 15 +++------------ .../swing/plaf/windows/WindowsLabelUI.java | 14 +++----------- .../plaf/windows/WindowsRadioButtonUI.java | 15 +++------------ .../plaf/windows/WindowsToggleButtonUI.java | 15 +++------------ 6 files changed, 21 insertions(+), 72 deletions(-) diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java index 4fb843543e3..06574d17641 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/AnimationController.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,8 +39,6 @@ import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.Prop; import com.sun.java.swing.plaf.windows.XPStyle.Skin; -import sun.awt.AppContext; - /** * A class to help mimic Vista theme animations. The only kind of * animation it handles for now is 'transition' animation (this seems @@ -68,8 +66,7 @@ final class AnimationController implements ActionListener, PropertyChangeListene Boolean.getBoolean("swing.disablevistaanimation"); - private static final Object ANIMATION_CONTROLLER_KEY = - new StringBuilder("ANIMATION_CONTROLLER_KEY"); + private static AnimationController animationController; private final Map> animationStateMap = new WeakHashMap>(); @@ -80,13 +77,10 @@ final class AnimationController implements ActionListener, PropertyChangeListene new javax.swing.Timer(1000/30, this); private static synchronized AnimationController getAnimationController() { - AppContext appContext = AppContext.getAppContext(); - Object obj = appContext.get(ANIMATION_CONTROLLER_KEY); - if (obj == null) { - obj = new AnimationController(); - appContext.put(ANIMATION_CONTROLLER_KEY, obj); + if (animationController == null) { + animationController = new AnimationController(); } - return (AnimationController) obj; + return animationController; } private AnimationController() { @@ -316,8 +310,7 @@ final class AnimationController implements ActionListener, PropertyChangeListene timer.stop(); UIManager.removePropertyChangeListener(this); synchronized (AnimationController.class) { - AppContext.getAppContext() - .put(ANIMATION_CONTROLLER_KEY, null); + animationController = null; } } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java index 0c86bd33e10..ee07276aa30 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java @@ -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 @@ -47,8 +47,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicGraphicsUtils; -import sun.awt.AppContext; - import static com.sun.java.swing.plaf.windows.TMSchema.Part; import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; @@ -69,20 +67,13 @@ public final class WindowsButtonUI extends BasicButtonUI private boolean defaults_initialized = false; - private static final Object WINDOWS_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsButtonUI(); // ******************************** // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - WindowsButtonUI windowsButtonUI = - (WindowsButtonUI) appContext.get(WINDOWS_BUTTON_UI_KEY); - if (windowsButtonUI == null) { - windowsButtonUI = new WindowsButtonUI(); - appContext.put(WINDOWS_BUTTON_UI_KEY, windowsButtonUI); - } - return windowsButtonUI; + return UI; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java index 3c869efc97b..7cb2490fd76 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java @@ -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 @@ -30,8 +30,6 @@ import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; -import sun.awt.AppContext; - /** * Windows check box. * @@ -43,7 +41,7 @@ public final class WindowsCheckBoxUI extends WindowsRadioButtonUI // of BasicCheckBoxUI because we want to pick up all the // painting changes made in WindowsRadioButtonUI. - private static final Object WINDOWS_CHECK_BOX_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsCheckBoxUI(); private static final String propertyPrefix = "CheckBox" + "."; @@ -53,14 +51,7 @@ public final class WindowsCheckBoxUI extends WindowsRadioButtonUI // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - WindowsCheckBoxUI windowsCheckBoxUI = - (WindowsCheckBoxUI) appContext.get(WINDOWS_CHECK_BOX_UI_KEY); - if (windowsCheckBoxUI == null) { - windowsCheckBoxUI = new WindowsCheckBoxUI(); - appContext.put(WINDOWS_CHECK_BOX_UI_KEY, windowsCheckBoxUI); - } - return windowsCheckBoxUI; + return UI; } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java index 403468af0af..4283f743b97 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java @@ -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 @@ -34,7 +34,6 @@ import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicLabelUI; -import sun.awt.AppContext; import sun.swing.MnemonicHandler; import sun.swing.SwingUtilities2; @@ -43,20 +42,13 @@ import sun.swing.SwingUtilities2; */ public final class WindowsLabelUI extends BasicLabelUI { - private static final Object WINDOWS_LABEL_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsLabelUI(); // ******************************** // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - WindowsLabelUI windowsLabelUI = - (WindowsLabelUI) appContext.get(WINDOWS_LABEL_UI_KEY); - if (windowsLabelUI == null) { - windowsLabelUI = new WindowsLabelUI(); - appContext.put(WINDOWS_LABEL_UI_KEY, windowsLabelUI); - } - return windowsLabelUI; + return UI; } @Override diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java index 2a784a470a4..c62a9ec8586 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java @@ -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 @@ -38,14 +38,12 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicGraphicsUtils; import javax.swing.plaf.basic.BasicRadioButtonUI; -import sun.awt.AppContext; - /** * Windows rendition of the component. */ public class WindowsRadioButtonUI extends BasicRadioButtonUI { - private static final Object WINDOWS_RADIO_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsRadioButtonUI(); protected int dashedRectGapX; protected int dashedRectGapY; @@ -60,14 +58,7 @@ public class WindowsRadioButtonUI extends BasicRadioButtonUI // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - WindowsRadioButtonUI windowsRadioButtonUI = - (WindowsRadioButtonUI) appContext.get(WINDOWS_RADIO_BUTTON_UI_KEY); - if (windowsRadioButtonUI == null) { - windowsRadioButtonUI = new WindowsRadioButtonUI(); - appContext.put(WINDOWS_RADIO_BUTTON_UI_KEY, windowsRadioButtonUI); - } - return windowsRadioButtonUI; + return UI; } // ******************************** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java index 5e94ebdb2cc..67eb5c1d6a0 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java @@ -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 @@ -38,8 +38,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicGraphicsUtils; import javax.swing.plaf.basic.BasicToggleButtonUI; -import sun.awt.AppContext; - /** * A Windows toggle button. * @@ -54,19 +52,12 @@ public final class WindowsToggleButtonUI extends BasicToggleButtonUI protected Color focusColor; - private static final Object WINDOWS_TOGGLE_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new WindowsToggleButtonUI(); private boolean defaults_initialized = false; public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - WindowsToggleButtonUI windowsToggleButtonUI = - (WindowsToggleButtonUI) appContext.get(WINDOWS_TOGGLE_BUTTON_UI_KEY); - if (windowsToggleButtonUI == null) { - windowsToggleButtonUI = new WindowsToggleButtonUI(); - appContext.put(WINDOWS_TOGGLE_BUTTON_UI_KEY, windowsToggleButtonUI); - } - return windowsToggleButtonUI; + return UI; } From 09ed8e66dc7a788763a2c7c24f54e93ec8eafedb Mon Sep 17 00:00:00 2001 From: Xiaolong Peng Date: Wed, 28 Jan 2026 21:28:16 +0000 Subject: [PATCH 033/215] 8376531: Genshen: Convert ShenandoahOldGeneration to use Atomic Reviewed-by: wkemper, shade --- .../gc/shenandoah/shenandoahOldGeneration.cpp | 22 +++++++++---------- .../gc/shenandoah/shenandoahOldGeneration.hpp | 10 ++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index aed768b9db1..d5e34d02b13 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -63,7 +63,7 @@ private: uint _nworkers; ShenandoahHeapRegion** _coalesce_and_fill_region_array; uint _coalesce_and_fill_region_count; - volatile bool _is_preempted; + Atomic _is_preempted; public: ShenandoahConcurrentCoalesceAndFillTask(uint nworkers, @@ -88,7 +88,7 @@ public: if (!r->oop_coalesce_and_fill(true)) { // Coalesce and fill has been preempted - AtomicAccess::store(&_is_preempted, true); + _is_preempted.store_relaxed(true); return; } } @@ -96,7 +96,7 @@ public: // Value returned from is_completed() is only valid after all worker thread have terminated. bool is_completed() { - return !AtomicAccess::load(&_is_preempted); + return !_is_preempted.load_relaxed(); } }; @@ -147,23 +147,23 @@ void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { void ShenandoahOldGeneration::reset_promoted_expended() { shenandoah_assert_heaplocked_or_safepoint(); - AtomicAccess::store(&_promoted_expended, static_cast(0)); - AtomicAccess::store(&_promotion_failure_count, static_cast(0)); - AtomicAccess::store(&_promotion_failure_words, static_cast(0)); + _promoted_expended.store_relaxed(0); + _promotion_failure_count.store_relaxed(0); + _promotion_failure_words.store_relaxed(0); } size_t ShenandoahOldGeneration::expend_promoted(size_t increment) { shenandoah_assert_heaplocked_or_safepoint(); assert(get_promoted_expended() + increment <= get_promoted_reserve(), "Do not expend more promotion than budgeted"); - return AtomicAccess::add(&_promoted_expended, increment); + return _promoted_expended.add_then_fetch(increment); } size_t ShenandoahOldGeneration::unexpend_promoted(size_t decrement) { - return AtomicAccess::sub(&_promoted_expended, decrement); + return _promoted_expended.sub_then_fetch(decrement); } size_t ShenandoahOldGeneration::get_promoted_expended() const { - return AtomicAccess::load(&_promoted_expended); + return _promoted_expended.load_relaxed(); } bool ShenandoahOldGeneration::can_allocate(const ShenandoahAllocRequest &req) const { @@ -582,8 +582,8 @@ void ShenandoahOldGeneration::handle_failed_evacuation() { } void ShenandoahOldGeneration::handle_failed_promotion(Thread* thread, size_t size) { - AtomicAccess::inc(&_promotion_failure_count); - AtomicAccess::add(&_promotion_failure_words, size); + _promotion_failure_count.add_then_fetch(1UL); + _promotion_failure_words.and_then_fetch(size); LogTarget(Debug, gc, plab) lt; LogStream ls(lt); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 633d2c9f617..5ebad461f3c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -64,7 +64,7 @@ private: // is therefore always accessed through atomic operations. This is increased when a // PLAB is allocated for promotions. The value is decreased by the amount of memory // remaining in a PLAB when it is retired. - size_t _promoted_expended; + Atomic _promoted_expended; // Represents the quantity of live bytes we expect to promote during the next GC cycle, either by // evacuation or by promote-in-place. This value is used by the young heuristic to trigger mixed collections. @@ -78,8 +78,8 @@ private: // Keep track of the number and size of promotions that failed. Perhaps we should use this to increase // the size of the old generation for the next collection cycle. - size_t _promotion_failure_count; - size_t _promotion_failure_words; + Atomic _promotion_failure_count; + Atomic _promotion_failure_words; // During construction of the collection set, we keep track of regions that are eligible // for promotion in place. These fields track the count of those humongous and regular regions. @@ -126,8 +126,8 @@ public: size_t get_promoted_expended() const; // Return the count and size (in words) of failed promotions since the last reset - size_t get_promotion_failed_count() const { return AtomicAccess::load(&_promotion_failure_count); } - size_t get_promotion_failed_words() const { return AtomicAccess::load(&_promotion_failure_words); } + size_t get_promotion_failed_count() const { return _promotion_failure_count.load_relaxed(); } + size_t get_promotion_failed_words() const { return _promotion_failure_words.load_relaxed(); } // Test if there is enough memory reserved for this promotion bool can_promote(size_t requested_bytes) const { From 2529e2fe8dfe9685033bb0ae558266b8bc3cf95c Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Thu, 29 Jan 2026 02:30:41 +0000 Subject: [PATCH 034/215] 8376169: JPopupMenu.setInvoker(null) causes NPE Reviewed-by: aivanov, azvegint, prr, kizune --- .../share/classes/javax/swing/JPopupMenu.java | 6 ++++-- .../jdk/javax/swing/JPopupMenu/TestPopupInvoker.java | 12 ++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java index 38bde1a97fa..1b04e8c6169 100644 --- a/src/java.desktop/share/classes/javax/swing/JPopupMenu.java +++ b/src/java.desktop/share/classes/javax/swing/JPopupMenu.java @@ -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 @@ -960,7 +960,9 @@ public class JPopupMenu extends JComponent implements Accessible,MenuElement { if (oldInvoker != null) { oldInvoker.removePropertyChangeListener("ancestor", propListener); } - invoker.addPropertyChangeListener("ancestor", propListener); + if (invoker != null) { + invoker.addPropertyChangeListener("ancestor", propListener); + } ui.installUI(this); } invalidate(); diff --git a/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java b/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java index c8ddfe67999..a3dc658df0e 100644 --- a/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.java +++ b/test/jdk/javax/swing/JPopupMenu/TestPopupInvoker.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 @@ -23,13 +23,14 @@ /* * @test - * @bug 4938801 + * @bug 4938801 8376169 * @key headful * @summary Verifies popup is removed when the component is removed * @run main TestPopupInvoker */ import java.awt.BorderLayout; +import java.awt.Component; import java.awt.Container; import java.awt.Robot; import java.util.concurrent.CountDownLatch; @@ -48,6 +49,7 @@ public class TestPopupInvoker { static JFrame frame; static JLabel label; static Container pane; + static volatile Component invoker; private static final CountDownLatch popupShown = new CountDownLatch(1); private static final CountDownLatch popupHidden = new CountDownLatch(1); @@ -73,6 +75,7 @@ public class TestPopupInvoker { @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { popupHidden.countDown(); + popupMenu.setInvoker(null); } @Override @@ -106,6 +109,11 @@ public class TestPopupInvoker { if (!popupHidden.await(1, SECONDS)) { throw new RuntimeException("Popup is visible after component is removed"); } + + SwingUtilities.invokeAndWait(() -> invoker = popupMenu.getInvoker()); + if (invoker != null) { + throw new RuntimeException("Invoker is not null"); + } } finally { SwingUtilities.invokeAndWait(() -> { if (frame != null) { From 62c7e9aefd4320d9d0cd8fa10610f59abb4de670 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 29 Jan 2026 04:49:56 +0000 Subject: [PATCH 035/215] 8376423: Test javax/swing/plaf/metal/MetalUtils/bug6190373.java failed: ClassCastException: class java.lang.Character cannot be cast to class javax.swing.Painter Reviewed-by: aivanov, tr --- .../share/classes/javax/swing/UIManager.java | 28 ++--- .../swing/plaf/metal/DefaultMetalTheme.java | 7 +- .../classes/sun/swing/SwingAccessor.java | 29 ++++- .../classes/sun/swing/SwingUtilities2.java | 8 +- .../javax/swing/UIManager/Test6657026.java | 60 ---------- .../plaf/metal/MetalUtils/bug6190373.java | 112 ------------------ 6 files changed, 45 insertions(+), 199 deletions(-) delete mode 100644 test/jdk/javax/swing/UIManager/Test6657026.java delete mode 100644 test/jdk/javax/swing/plaf/metal/MetalUtils/bug6190373.java diff --git a/src/java.desktop/share/classes/javax/swing/UIManager.java b/src/java.desktop/share/classes/javax/swing/UIManager.java index 7a2ae226f33..69063c562e6 100644 --- a/src/java.desktop/share/classes/javax/swing/UIManager.java +++ b/src/java.desktop/share/classes/javax/swing/UIManager.java @@ -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 @@ -59,6 +59,7 @@ import java.util.Objects; import sun.awt.AppContext; import sun.awt.AWTAccessor; +import sun.swing.SwingAccessor; /** * {@code UIManager} manages the current look and feel, the set of @@ -233,8 +234,9 @@ public class UIManager implements Serializable */ public UIManager() {} + private static final LAFState LAF_STATE = new LAFState(); /** - * Return the LAFState object, lazily create one if necessary. + * Return the LAFState object. * All access to the LAFState fields is done via this method, * for example: *

@@ -242,22 +244,18 @@ public class UIManager implements Serializable
      * 
*/ private static LAFState getLAFState() { - LAFState rv = (LAFState)SwingUtilities.appContextGet( - SwingUtilities2.LAF_STATE_KEY); - if (rv == null) { - synchronized (classLock) { - rv = (LAFState)SwingUtilities.appContextGet( - SwingUtilities2.LAF_STATE_KEY); - if (rv == null) { - SwingUtilities.appContextPut( - SwingUtilities2.LAF_STATE_KEY, - (rv = new LAFState())); - } - } + synchronized (classLock) { + return LAF_STATE; } - return rv; } + static { + SwingAccessor.setLAFStateAccessor(UIManager::isLafStateInitialized); + } + + private static boolean isLafStateInitialized() { + return LAF_STATE.initialized; + } /* Keys used in the swing.properties properties file. * See loadUserProperties(), initialize(). diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/DefaultMetalTheme.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/DefaultMetalTheme.java index 8ee862fb0b3..d64267676c3 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/DefaultMetalTheme.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/DefaultMetalTheme.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,7 +29,7 @@ import javax.swing.plaf.*; import javax.swing.*; import java.awt.*; -import sun.awt.AppContext; +import sun.swing.SwingAccessor; import sun.swing.SwingUtilities2; /** @@ -153,8 +153,7 @@ public class DefaultMetalTheme extends MetalTheme { static int getDefaultFontStyle(int key) { if (key != WINDOW_TITLE_FONT) { Object boldMetal = null; - if (AppContext.getAppContext().get( - SwingUtilities2.LAF_STATE_KEY) != null) { + if (SwingAccessor.getLAFStateAccessor().lafStateIsInitialized()) { // Only access the boldMetal key if a look and feel has // been loaded, otherwise we'll trigger loading the look // and feel. diff --git a/src/java.desktop/share/classes/sun/swing/SwingAccessor.java b/src/java.desktop/share/classes/sun/swing/SwingAccessor.java index 5c561de4a42..563cc038ae9 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingAccessor.java +++ b/src/java.desktop/share/classes/sun/swing/SwingAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, 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 @@ -133,6 +133,33 @@ public final class SwingAccessor { KeyStroke create(); } + /** + * An accessor for the LAFState class state. + */ + public interface LAFStateAccessor { + boolean lafStateIsInitialized(); + } + + private static LAFStateAccessor lafStateAccessor; + /** + * Set an accessor object for the LAFState class. + */ + public static void setLAFStateAccessor(LAFStateAccessor accessor) { + lafStateAccessor = accessor; + } + + /** + * Retrieve the accessor object for the LAFState class. + */ + public static LAFStateAccessor getLAFStateAccessor() { + var access = lafStateAccessor; + if (access == null) { + ensureClassInitialized(UIManager.class); + access = lafStateAccessor; + } + return access; + } + /** * The javax.swing.JComponent class accessor object. */ diff --git a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java index 20a07c608a0..faf31e0b426 100644 --- a/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java +++ b/src/java.desktop/share/classes/sun/swing/SwingUtilities2.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -120,12 +120,6 @@ import static java.awt.geom.AffineTransform.TYPE_TRANSLATION; * */ public class SwingUtilities2 { - /** - * The {@code AppContext} key for our one {@code LAFState} - * instance. - */ - public static final Object LAF_STATE_KEY = - new StringBuffer("LookAndFeel State"); public static final Object MENU_SELECTION_MANAGER_LISTENER_KEY = new StringBuffer("MenuSelectionManager listener key"); diff --git a/test/jdk/javax/swing/UIManager/Test6657026.java b/test/jdk/javax/swing/UIManager/Test6657026.java deleted file mode 100644 index 5ce1ea73de4..00000000000 --- a/test/jdk/javax/swing/UIManager/Test6657026.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 6657026 - * @summary Tests shared UIManager in different application contexts - * @author Sergey Malenkov - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.UIManager; -import javax.swing.UIManager.LookAndFeelInfo; - -public class Test6657026 implements Runnable { - - public static void main(String[] args) throws Exception { - if (UIManager.getInstalledLookAndFeels().length == 0) { - throw new Error("unexpected amount of look&feels"); - } - UIManager.setInstalledLookAndFeels(new LookAndFeelInfo[0]); - if (UIManager.getInstalledLookAndFeels().length != 0) { - throw new Error("unexpected amount of look&feels"); - } - - ThreadGroup group = new ThreadGroup("$$$"); - Thread thread = new Thread(group, new Test6657026()); - thread.start(); - thread.join(); - } - - public void run() { - SunToolkit.createNewAppContext(); - if (UIManager.getInstalledLookAndFeels().length == 0) { - throw new Error("shared look&feels"); - } - } -} diff --git a/test/jdk/javax/swing/plaf/metal/MetalUtils/bug6190373.java b/test/jdk/javax/swing/plaf/metal/MetalUtils/bug6190373.java deleted file mode 100644 index 07211c7a569..00000000000 --- a/test/jdk/javax/swing/plaf/metal/MetalUtils/bug6190373.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.awt.Graphics; -import java.awt.image.BufferedImage; -import java.util.concurrent.CyclicBarrier; - -import javax.swing.JButton; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; - -import sun.awt.AppContext; -import sun.awt.SunToolkit; - -import static javax.swing.UIManager.getInstalledLookAndFeels; - -/** - * @test - * @bug 6190373 - * @summary Tests 6190373 - * @author Scott Violet - * @modules java.desktop/sun.awt - */ -public final class bug6190373 { - - private static AppContext app1; - private static AppContext app2; - private static final int LOOP_COUNT = 10000; - private static final CyclicBarrier barrier = new CyclicBarrier(2); - - public static void main(final String[] args) throws Exception { - final Thread t1 = new Thread(new ThreadGroup("firstGroup"), () -> { - app1 = SunToolkit.createNewAppContext(); - test(true); - }); - final Thread t2 = new Thread(new ThreadGroup("secondGroup"), () -> { - app2 = SunToolkit.createNewAppContext(); - test(false); - }); - - t1.start(); - t2.start(); - t1.join(); - t2.join(); - app1.dispose(); - app2.dispose(); - } - - private static void test(final boolean lock) { - for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) { - try { - SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf)); - barrier.await(); - SwingUtilities.invokeAndWait(() -> slam(lock)); - barrier.await(); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - } - - private static void slam(final boolean lock) { - JButton button = new JButton("HI"); - button.setSize(100, 100); - BufferedImage image = new BufferedImage(100, 100, - BufferedImage.TYPE_INT_RGB); - for (int i = 0; i < LOOP_COUNT; i++) { - Graphics g = image.getGraphics(); - if (lock) { - synchronized (button.getTreeLock()) { - button.paint(g); - } - } else { - button.paint(g); - } - g.dispose(); - } - } - - private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) { - try { - UIManager.setLookAndFeel(laf.getClassName()); - System.out.println("LookAndFeel: " + laf.getClassName()); - } catch (final UnsupportedLookAndFeelException ignored){ - System.out.println("Unsupported LookAndFeel: " + laf.getClassName()); - } catch (ClassNotFoundException | InstantiationException | - IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} From 19c6fdf11b01308e9f99ce5666bfffcfbc453de3 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Thu, 29 Jan 2026 06:34:02 +0000 Subject: [PATCH 036/215] 8376290: SocketChannel.finishConnect() contains confusing "getsockopt" in exception message for a failed connect() on Windows Reviewed-by: alanb --- .../unix/native/libnet/net_util_md.c | 13 +- .../windows/native/libnet/net_util_md.c | 30 ++-- src/java.base/windows/native/libnio/ch/Net.c | 4 +- .../Selector/ConnectionRefusedMessage.java | 128 ++++++++++++++++++ 4 files changed, 153 insertions(+), 22 deletions(-) create mode 100644 test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java diff --git a/src/java.base/unix/native/libnet/net_util_md.c b/src/java.base/unix/native/libnet/net_util_md.c index 1496be5f5c6..48cc1a7bb02 100644 --- a/src/java.base/unix/native/libnet/net_util_md.c +++ b/src/java.base/unix/native/libnet/net_util_md.c @@ -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 @@ -68,13 +68,14 @@ NET_ThrowByNameWithLastError(JNIEnv *env, const char *name, void NET_ThrowNew(JNIEnv *env, int errorNumber, char *msg) { char fullMsg[512]; - if (!msg) { - msg = "no further information"; - } switch(errorNumber) { case EBADF: - jio_snprintf(fullMsg, sizeof(fullMsg), "socket closed: %s", msg); - JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", fullMsg); + if (msg == NULL) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); + } else { + jio_snprintf(fullMsg, sizeof(fullMsg), "socket closed: %s", msg); + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", fullMsg); + } break; default: errno = errorNumber; diff --git a/src/java.base/windows/native/libnet/net_util_md.c b/src/java.base/windows/native/libnet/net_util_md.c index bac3d1438ab..5abdd8d4c2e 100644 --- a/src/java.base/windows/native/libnet/net_util_md.c +++ b/src/java.base/windows/native/libnet/net_util_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, 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 @@ -139,13 +139,6 @@ NET_ThrowNew(JNIEnv *env, int errorNum, char *msg) return; } - /* - * Default message text if not provided - */ - if (!msg) { - msg = "no further information"; - } - /* * Check table for known winsock errors */ @@ -163,13 +156,22 @@ NET_ThrowNew(JNIEnv *env, int errorNum, char *msg) */ if (i < table_size) { excP = (char *)winsock_errors[i].exc; - jio_snprintf(fullMsg, sizeof(fullMsg), "%s: %s", - (char *)winsock_errors[i].errString, msg); + if (msg == NULL) { + jio_snprintf(fullMsg, sizeof(fullMsg), "%s", + (char *)winsock_errors[i].errString); + } else { + jio_snprintf(fullMsg, sizeof(fullMsg), "%s: %s", + (char *)winsock_errors[i].errString, msg); + } } else { - jio_snprintf(fullMsg, sizeof(fullMsg), - "Unrecognized Windows Sockets error: %d: %s", - errorNum, msg); - + if (msg == NULL) { + jio_snprintf(fullMsg, sizeof(fullMsg), + "Unrecognized Windows Sockets error: %d", errorNum); + } else { + jio_snprintf(fullMsg, sizeof(fullMsg), + "Unrecognized Windows Sockets error: %d: %s", + errorNum, msg); + } } /* diff --git a/src/java.base/windows/native/libnio/ch/Net.c b/src/java.base/windows/native/libnio/ch/Net.c index 814f502c48a..adfd67b5017 100644 --- a/src/java.base/windows/native/libnio/ch/Net.c +++ b/src/java.base/windows/native/libnio/ch/Net.c @@ -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 @@ -733,7 +733,7 @@ Java_sun_nio_ch_Net_pollConnect(JNIEnv* env, jclass this, jobject fdo, jlong tim NET_ThrowNew(env, lastError, "getsockopt"); } } else if (optError != NO_ERROR) { - NET_ThrowNew(env, optError, "getsockopt"); + NET_ThrowNew(env, optError, NULL); } return JNI_FALSE; } diff --git a/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java b/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java new file mode 100644 index 00000000000..5e7b6395a66 --- /dev/null +++ b/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java @@ -0,0 +1,128 @@ +/* + * 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. + */ + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.time.Duration; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeFalse; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/* + * @test + * @bug 8376290 + * @summary Verify that when a SocketChannel is registered with a Selector + * with an interest in CONNECT operation, then SocketChannel.finishConnect() + * throws the correct exception message, if the connect() fails + * @run junit ${test.main.class} + */ +class ConnectionRefusedMessage { + + /* + * On a non-blocking SocketChannel, registered with a Selector, this test method + * attempts a SocketChannel.connect() against an address that is expected to return + * Connection refused. The test then calls SocketChannel.finishConnect() when the + * Selector makes available the ready key for this connect operation and expects + * that finishConnect() throws a ConnectException with the expected exception message. + */ + @Test + void testFinishConnect() throws Exception { + // find a suitable address against which the connect() attempt + // will result in a Connection refused exception + final InetSocketAddress destAddr = findSuitableRefusedAddress(); + // skip the test if we couldn't find a port which would raise a connection refused error + assumeTrue(destAddr != null, + "couldn't find a suitable port which will generate a connection refused error"); + try (Selector selector = Selector.open(); + SocketChannel sc = SocketChannel.open()) { + + // non-blocking + sc.configureBlocking(false); + sc.register(selector, SelectionKey.OP_CONNECT); + + System.err.println("establishing connection to " + destAddr); + boolean connected = sc.connect(destAddr); + // this test checks the exception message of a ConnectException, so it's + // OK to skip the test if something unexpectedly accepted the connection + assumeFalse(connected, "unexpectedly connected to " + destAddr); + // wait for ready ops + int numReady = selector.select(Duration.ofMinutes(10).toMillis()); + System.err.println("Num ready keys = " + numReady); + for (SelectionKey readyKey : selector.selectedKeys()) { + System.err.println("ready key: " + readyKey); + assertTrue(readyKey.isConnectable(), "unexpected key, readyOps = " + + readyKey.readyOps()); + readyKey.cancel(); + try { + boolean success = sc.finishConnect(); + // this test checks the exception message of a ConnectException, so it's + // OK to skip the test if something unexpectedly accepted the connection + assumeFalse(success, "unexpectedly connected to " + destAddr); + // this test doesn't expect finishConnect() to return normally + // with a return value of false + fail("ConnectException was not thrown"); + } catch (ConnectException ce) { + System.err.println("got (expected) ConnectException - " + ce); + // verify exception message + if (!"Connection refused".equals(ce.getMessage())) { + // propagate the original exception + fail("unexpected exception message: " + ce.getMessage(), ce); + } + } + } + } + } + + // Try to find a suitable port to provoke a "Connection Refused" error. + private static InetSocketAddress findSuitableRefusedAddress() throws IOException { + final InetAddress loopbackAddr = InetAddress.getLoopbackAddress(); + // Ports 47, 51, 61 are in the IANA reserved port list, and + // are currently unassigned to any specific service. + // We use them here on the assumption that there won't be + // any service listening on them. + InetSocketAddress destAddr = new InetSocketAddress(loopbackAddr, 47); + try (SocketChannel sc1 = SocketChannel.open(destAddr)) { + // we managed to connect (unexpectedly), let's try the next reserved port + destAddr = new InetSocketAddress(loopbackAddr, 51); + try (SocketChannel sc2 = SocketChannel.open(destAddr)) { + } + // we managed to connect (unexpectedly again), let's try the next reserved port + // as a last attempt + destAddr = new InetSocketAddress(loopbackAddr, 61); + try (SocketChannel sc3 = SocketChannel.open(destAddr)) { + } + return null; + } catch (ConnectException x) { + } + // the address which will generate a connection refused, when a connection is attempted + return destAddr; + } +} From 06d1345f2913830c273b9546c997e877f7958113 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 29 Jan 2026 08:39:10 +0000 Subject: [PATCH 037/215] 8373026: C2 SuperWord and Vector API: vector algorithms test and benchmark Co-authored-by: Otmar Ertl Reviewed-by: vlivanov, jbhateja, psandoz, xgong --- .../vectorization/TestVectorAlgorithms.java | 556 +++++++++++++ .../vectorization/VectorAlgorithmsImpl.java | 774 +++++++++++++++++ .../bench/vm/compiler/VectorAlgorithms.java | 274 +++++++ .../vm/compiler/VectorAlgorithmsImpl.java | 775 ++++++++++++++++++ 4 files changed, 2379 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java create mode 100644 test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java create mode 100644 test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java create mode 100644 test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java diff --git a/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java new file mode 100644 index 00000000000..e02563c2fc2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java @@ -0,0 +1,556 @@ +/* + * 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 id=vanilla + * @bug 8373026 + * @summary Test auto vectorization and Vector API with some vector + * algorithms. Related benchmark: VectorAlgorithms.java + * @library /test/lib / + * @modules jdk.incubator.vector + * @run driver ${test.main.class} + */ + +/* + * @test id=noSuperWord + * @bug 8373026 + * @library /test/lib / + * @modules jdk.incubator.vector + * @run driver ${test.main.class} -XX:-UseSuperWord + */ + +/* + * @test id=noOptimizeFill + * @bug 8373026 + * @library /test/lib / + * @modules jdk.incubator.vector + * @run driver ${test.main.class} -XX:-OptimizeFill + */ + +package compiler.vectorization; + +import java.util.Map; +import java.util.HashMap; +import jdk.test.lib.Utils; +import java.util.Random; +import java.lang.foreign.*; + +import compiler.lib.ir_framework.*; +import compiler.lib.generators.*; +import static compiler.lib.generators.Generators.G; +import compiler.lib.verify.*; + +/** + * The goal of this benchmark is to show the power of auto vectorization + * and the Vector API. + * + * Please only modify this benchark in synchronization with the JMH benchmark: + * micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java + */ +public class TestVectorAlgorithms { + private static final Random RANDOM = Utils.getRandomInstance(); + private static final RestrictableGenerator INT_GEN = Generators.G.ints(); + + interface TestFunction { + Object run(int i); + } + + Map> testGroups = new HashMap>(); + + VectorAlgorithmsImpl.Data d; + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + framework.addFlags("--add-modules=jdk.incubator.vector", + "-XX:CompileCommand=inline,*VectorAlgorithmsImpl*::*"); + framework.addFlags(args); + framework.start(); + } + + public TestVectorAlgorithms () { + testGroups.put("fillI", new HashMap()); + testGroups.get("fillI").put("fillI_loop", i -> { return fillI_loop(d.rI1); }); + testGroups.get("fillI").put("fillI_VectorAPI", i -> { return fillI_VectorAPI(d.rI2); }); + testGroups.get("fillI").put("fillI_Arrays", i -> { return fillI_Arrays(d.rI3); }); + + testGroups.put("iotaI", new HashMap()); + testGroups.get("iotaI").put("iotaI_loop", i -> { return iotaI_loop(d.rI1); }); + testGroups.get("iotaI").put("iotaI_VectorAPI", i -> { return iotaI_VectorAPI(d.rI2); }); + + testGroups.put("copyI", new HashMap()); + testGroups.get("copyI").put("copyI_loop", i -> { return copyI_loop(d.aI, d.rI1); }); + testGroups.get("copyI").put("copyI_VectorAPI", i -> { return copyI_VectorAPI(d.aI, d.rI2); }); + testGroups.get("copyI").put("copyI_System_arraycopy", i -> { return copyI_System_arraycopy(d.aI, d.rI3); }); + + testGroups.put("mapI", new HashMap()); + testGroups.get("mapI").put("mapI_loop", i -> { return mapI_loop(d.aI, d.rI1); }); + testGroups.get("mapI").put("mapI_VectorAPI", i -> { return mapI_VectorAPI(d.aI, d.rI2); }); + + testGroups.put("reduceAddI", new HashMap()); + testGroups.get("reduceAddI").put("reduceAddI_loop", i -> { return reduceAddI_loop(d.aI); }); + testGroups.get("reduceAddI").put("reduceAddI_reassociate", i -> { return reduceAddI_reassociate(d.aI); }); + testGroups.get("reduceAddI").put("reduceAddI_VectorAPI_naive", i -> { return reduceAddI_VectorAPI_naive(d.aI); }); + testGroups.get("reduceAddI").put("reduceAddI_VectorAPI_reduction_after_loop", i -> { return reduceAddI_VectorAPI_reduction_after_loop(d.aI); }); + + testGroups.put("dotProductF", new HashMap()); + testGroups.get("dotProductF").put("dotProductF_loop", i -> { return dotProductF_loop(d.aF, d.bF); }); + testGroups.get("dotProductF").put("dotProductF_VectorAPI_naive", i -> { return dotProductF_VectorAPI_naive(d.aF, d.bF); }); + testGroups.get("dotProductF").put("dotProductF_VectorAPI_reduction_after_loop", i -> { return dotProductF_VectorAPI_reduction_after_loop(d.aF, d.bF); }); + + testGroups.put("hashCodeB", new HashMap()); + testGroups.get("hashCodeB").put("hashCodeB_loop", i -> { return hashCodeB_loop(d.aB); }); + testGroups.get("hashCodeB").put("hashCodeB_Arrays", i -> { return hashCodeB_Arrays(d.aB); }); + testGroups.get("hashCodeB").put("hashCodeB_VectorAPI_v1", i -> { return hashCodeB_VectorAPI_v1(d.aB); }); + testGroups.get("hashCodeB").put("hashCodeB_VectorAPI_v2", i -> { return hashCodeB_VectorAPI_v2(d.aB); }); + + testGroups.put("scanAddI", new HashMap()); + testGroups.get("scanAddI").put("scanAddI_loop", i -> { return scanAddI_loop(d.aI, d.rI1); }); + testGroups.get("scanAddI").put("scanAddI_loop_reassociate", i -> { return scanAddI_loop_reassociate(d.aI, d.rI2); }); + testGroups.get("scanAddI").put("scanAddI_VectorAPI_permute_add", i -> { return scanAddI_VectorAPI_permute_add(d.aI, d.rI4); }); + + testGroups.put("findMinIndexI", new HashMap()); + testGroups.get("findMinIndexI").put("findMinIndexI_loop", i -> { return findMinIndexI_loop(d.aI); }); + testGroups.get("findMinIndexI").put("findMinIndexI_VectorAPI", i -> { return findMinIndexI_VectorAPI(d.aI); }); + + testGroups.put("findI", new HashMap()); + testGroups.get("findI").put("findI_loop", i -> { return findI_loop(d.aI, d.eI[i]); }); + testGroups.get("findI").put("findI_VectorAPI", i -> { return findI_VectorAPI(d.aI, d.eI[i]); }); + + testGroups.put("reverseI", new HashMap()); + testGroups.get("reverseI").put("reverseI_loop", i -> { return reverseI_loop(d.aI, d.rI1); }); + testGroups.get("reverseI").put("reverseI_VectorAPI", i -> { return reverseI_VectorAPI(d.aI, d.rI2); }); + + testGroups.put("filterI", new HashMap()); + testGroups.get("filterI").put("filterI_loop", i -> { return filterI_loop(d.aI, d.rI1, d.eI[i]); }); + testGroups.get("filterI").put("filterI_VectorAPI", i -> { return filterI_VectorAPI(d.aI, d.rI2, d.eI[i]); }); + + testGroups.put("reduceAddIFieldsX4", new HashMap()); + testGroups.get("reduceAddIFieldsX4").put("reduceAddIFieldsX4_loop", i -> { return reduceAddIFieldsX4_loop(d.oopsX4, d.memX4); }); + testGroups.get("reduceAddIFieldsX4").put("reduceAddIFieldsX4_VectorAPI", i -> { return reduceAddIFieldsX4_VectorAPI(d.oopsX4, d.memX4); }); + + testGroups.put("lowerCaseB", new HashMap()); + testGroups.get("lowerCaseB").put("lowerCaseB_loop", i -> { return lowerCaseB_loop(d.strB, d.rB1); }); + testGroups.get("lowerCaseB").put("lowerCaseB_VectorAPI_v1", i -> { return lowerCaseB_VectorAPI_v1(d.strB, d.rB2); }); + testGroups.get("lowerCaseB").put("lowerCaseB_VectorAPI_v2", i -> { return lowerCaseB_VectorAPI_v2(d.strB, d.rB3); }); + } + + @Warmup(100) + @Run(test = {"fillI_loop", + "fillI_VectorAPI", + "fillI_Arrays", + "iotaI_loop", + "iotaI_VectorAPI", + "copyI_loop", + "copyI_VectorAPI", + "copyI_System_arraycopy", + "mapI_loop", + "mapI_VectorAPI", + "reduceAddI_loop", + "reduceAddI_reassociate", + "reduceAddI_VectorAPI_naive", + "reduceAddI_VectorAPI_reduction_after_loop", + "dotProductF_loop", + "dotProductF_VectorAPI_naive", + "dotProductF_VectorAPI_reduction_after_loop", + "hashCodeB_loop", + "hashCodeB_Arrays", + "hashCodeB_VectorAPI_v1", + "hashCodeB_VectorAPI_v2", + "scanAddI_loop", + "scanAddI_loop_reassociate", + "scanAddI_VectorAPI_permute_add", + "findMinIndexI_loop", + "findMinIndexI_VectorAPI", + "findI_loop", + "findI_VectorAPI", + "reverseI_loop", + "reverseI_VectorAPI", + "filterI_loop", + "filterI_VectorAPI", + "reduceAddIFieldsX4_loop", + "reduceAddIFieldsX4_VectorAPI", + "lowerCaseB_loop", + "lowerCaseB_VectorAPI_v1", + "lowerCaseB_VectorAPI_v2"}) + public void runTests(RunInfo info) { + // Repeat many times, so that we also have multiple iterations for post-warmup to potentially recompile + int iters = info.isWarmUp() ? 1 : 20; + for (int iter = 0; iter < iters; iter++) { + // Set up random inputs, random size is important to stress tails. + int size = 100_000 + RANDOM.nextInt(10_000); + int seed = RANDOM.nextInt(); + int numXObjects = 10_000; + d = new VectorAlgorithmsImpl.Data(size, seed, numXObjects); + + // Run all tests + for (Map.Entry> group_entry : testGroups.entrySet()) { + String group_name = group_entry.getKey(); + Map group = group_entry.getValue(); + Object gold = null; + String gold_name = "NONE"; + for (Map.Entry entry : group.entrySet()) { + String name = entry.getKey(); + TestFunction test = entry.getValue(); + Object result = test.run(iter); + if (gold == null) { + gold = result; + gold_name = name; + } else { + try { + Verify.checkEQ(gold, result); + } catch (VerifyException e) { + throw new RuntimeException("Verify.checkEQ failed for group " + group_name + + ", gold " + gold_name + ", test " + name, e); + } + } + } + } + } + } + + @Test + @IR(counts = {IRNode.REPLICATE_I, "= 1", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIfAnd = {"UseSuperWord", "true", "OptimizeFill", "false"}) + @IR(counts = {".*CallLeafNoFP.*jint_fill.*", "= 1"}, + phase = CompilePhase.BEFORE_MATCHING, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"OptimizeFill", "true"}) + // By default, the fill intrinsic "jint_fill" is used, but we can disable + // the detection of the fill loop, and then we auto vectorize. + public Object fillI_loop(int[] r) { + return VectorAlgorithmsImpl.fillI_loop(r); + } + + @Test + @IR(counts = {IRNode.REPLICATE_I, "= 1", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object fillI_VectorAPI(int[] r) { + return VectorAlgorithmsImpl.fillI_VectorAPI(r); + } + + @Test + // Arrays.fill is not necessarily inlined, so we can't check + // for vectors in the IR. + public Object fillI_Arrays(int[] r) { + return VectorAlgorithmsImpl.fillI_Arrays(r); + } + + @Test + @IR(counts = {IRNode.POPULATE_INDEX, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"avx2", "true", "sve", "true"}, + applyIf = {"UseSuperWord", "true"}) + // Note: the Vector API example below can also vectorize for AVX, + // because it does not use a PopulateIndex. + public Object iotaI_loop(int[] r) { + return VectorAlgorithmsImpl.iotaI_loop(r); + } + + @Test + @IR(counts = {IRNode.ADD_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeature = {"sse4.1", "true"}) + // Note: also works with NEON/asimd, but only with TieredCompilation. + public Object iotaI_VectorAPI(int[] r) { + return VectorAlgorithmsImpl.iotaI_VectorAPI(r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"UseSuperWord", "true"}) + public Object copyI_loop(int[] a, int[] r) { + return VectorAlgorithmsImpl.copyI_loop(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object copyI_VectorAPI(int[] a, int[] r) { + return VectorAlgorithmsImpl.copyI_VectorAPI(a, r); + } + + @Test + @IR(counts = {".*CallLeafNoFP.*jint_disjoint_arraycopy.*", "= 1"}, + phase = CompilePhase.BEFORE_MATCHING, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object copyI_System_arraycopy(int[] a, int[] r) { + return VectorAlgorithmsImpl.copyI_System_arraycopy(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.MUL_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"UseSuperWord", "true"}) + public Object mapI_loop(int[] a, int[] r) { + return VectorAlgorithmsImpl.mapI_loop(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.MUL_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object mapI_VectorAPI(int[] a, int[] r) { + return VectorAlgorithmsImpl.mapI_VectorAPI(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0", + IRNode.ADD_VI, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"UseSuperWord", "true"}) + public int reduceAddI_loop(int[] a) { + return VectorAlgorithmsImpl.reduceAddI_loop(a); + } + + @Test + public int reduceAddI_reassociate(int[] a) { + return VectorAlgorithmsImpl.reduceAddI_reassociate(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0"}, // reduceLanes inside loop + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public int reduceAddI_VectorAPI_naive(int[] a) { + return VectorAlgorithmsImpl.reduceAddI_VectorAPI_naive(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", + IRNode.ADD_REDUCTION_V, "> 0", + IRNode.MUL_VF, "> 0"}, + applyIfCPUFeature = {"sse4.1", "true"}, + applyIf = {"UseSuperWord", "true"}) + // See also TestReduction.floatAddDotProduct + public float dotProductF_loop(float[] a, float[] b) { + return VectorAlgorithmsImpl.dotProductF_loop(a, b); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", + IRNode.ADD_REDUCTION_V, "> 0", + IRNode.MUL_VF, "> 0"}, + applyIfCPUFeature = {"sse4.1", "true"}, + applyIf = {"UseSuperWord", "true"}) + public float dotProductF_VectorAPI_naive(float[] a, float[] b) { + return VectorAlgorithmsImpl.dotProductF_VectorAPI_naive(a, b); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_F, "> 0", + IRNode.ADD_REDUCTION_V, "> 0", + IRNode.MUL_VF, "> 0"}, + applyIfCPUFeature = {"sse4.1", "true"}, + applyIf = {"UseSuperWord", "true"}) + public float dotProductF_VectorAPI_reduction_after_loop(float[] a, float[] b) { + return VectorAlgorithmsImpl.dotProductF_VectorAPI_reduction_after_loop(a, b); + } + + @Test + public int hashCodeB_loop(byte[] a) { + return VectorAlgorithmsImpl.hashCodeB_loop(a); + } + + @Test + public int hashCodeB_Arrays(byte[] a) { + return VectorAlgorithmsImpl.hashCodeB_Arrays(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, IRNode.VECTOR_SIZE_8, "> 0", + IRNode.VECTOR_CAST_B2I, IRNode.VECTOR_SIZE_8, "> 0", + IRNode.MUL_VI, IRNode.VECTOR_SIZE_8, "> 0", + IRNode.ADD_VI, IRNode.VECTOR_SIZE_8, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0"}, + applyIfCPUFeature = {"avx2", "true"}) + public int hashCodeB_VectorAPI_v1(byte[] a) { + return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v1(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.MUL_VI, "> 0", + IRNode.ADD_VI, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0"}, + applyIfCPUFeature = {"avx2", "true"}) + public int hashCodeB_VectorAPI_v2(byte[] a) { + return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v2(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0", + IRNode.ADD_VI, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public int reduceAddI_VectorAPI_reduction_after_loop(int[] a) { + return VectorAlgorithmsImpl.reduceAddI_VectorAPI_reduction_after_loop(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", + IRNode.STORE_VECTOR, "= 0"}) + // Currently does not vectorize, but might in the future. + public Object scanAddI_loop(int[] a, int[] r) { + return VectorAlgorithmsImpl.scanAddI_loop(a, r); + } + + @Test + public Object scanAddI_loop_reassociate(int[] a, int[] r) { + return VectorAlgorithmsImpl.scanAddI_loop_reassociate(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.REARRANGE_VI, "> 0", + IRNode.AND_VI, "> 0", + IRNode.ADD_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}, + applyIf = {"MaxVectorSize", ">=64"}) + public Object scanAddI_VectorAPI_permute_add(int[] a, int[] r) { + return VectorAlgorithmsImpl.scanAddI_VectorAPI_permute_add(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0"}) + // Currently does not vectorize, but might in the future. + public int findMinIndexI_loop(int[] a) { + return VectorAlgorithmsImpl.findMinIndexI_loop(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_BLEND_I, "> 0", + IRNode.MIN_REDUCTION_V, "> 0", + IRNode.ADD_VI, "> 0"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + public int findMinIndexI_VectorAPI(int[] a) { + return VectorAlgorithmsImpl.findMinIndexI_VectorAPI(a); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0"}) + // Currently does not vectorize, but might in the future. + public int findI_loop(int[] a, int e) { + return VectorAlgorithmsImpl.findI_loop(a, e); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_TEST, "> 0"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + public int findI_VectorAPI(int[] a, int e) { + return VectorAlgorithmsImpl.findI_VectorAPI(a, e); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", + IRNode.STORE_VECTOR, "= 0"}) + // Currently does not vectorize, but might in the future. + public Object reverseI_loop(int[] a, int[] r) { + return VectorAlgorithmsImpl.reverseI_loop(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.REARRANGE_VI, "> 0", + IRNode.AND_VI, "> 0", + IRNode.STORE_VECTOR, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object reverseI_VectorAPI(int[] a, int[] r) { + return VectorAlgorithmsImpl.reverseI_VectorAPI(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0", + IRNode.STORE_VECTOR, "= 0"}) + public Object filterI_loop(int[] a, int[] r, int threshold) { + return VectorAlgorithmsImpl.filterI_loop(a, r, threshold); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_TEST, "> 0", + IRNode.COMPRESS_VI, "> 0", + IRNode.STORE_VECTOR_MASKED, "> 0"}, + applyIfCPUFeature = {"avx2", "true"}) + public Object filterI_VectorAPI(int[] a, int[] r, int threshold) { + return VectorAlgorithmsImpl.filterI_VectorAPI(a, r, threshold); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "= 0"}) + // Currently does not vectorize, but might in the future. + public int reduceAddIFieldsX4_loop(int[] oops, int[] mem) { + return VectorAlgorithmsImpl.reduceAddIFieldsX4_loop(oops, mem); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_I, "> 0", + IRNode.VECTOR_MASK_CMP, "> 0", + IRNode.VECTOR_TEST, "> 0", + IRNode.LOAD_VECTOR_GATHER_MASKED, "> 0", + IRNode.OR_V_MASK, "> 0", + IRNode.ADD_VI, "> 0", + IRNode.ADD_REDUCTION_VI, "> 0"}, + applyIfCPUFeatureOr = {"avx512", "true", "sve", "true"}) + public int reduceAddIFieldsX4_VectorAPI(int[] oops, int[] mem) { + return VectorAlgorithmsImpl.reduceAddIFieldsX4_VectorAPI(oops, mem); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "= 0"}) + // Currently does not vectorize, but might in the future. + public Object lowerCaseB_loop(byte[] a, byte[] r) { + return VectorAlgorithmsImpl.lowerCaseB_loop(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object lowerCaseB_VectorAPI_v1(byte[] a, byte[] r) { + return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v1(a, r); + } + + @Test + @IR(counts = {IRNode.LOAD_VECTOR_B, "> 0", + IRNode.ADD_VB, "> 0"}, + applyIfCPUFeatureOr = {"sse4.1", "true", "asimd", "true"}) + public Object lowerCaseB_VectorAPI_v2(byte[] a, byte[] r) { + return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v2(a, r); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java b/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java new file mode 100644 index 00000000000..2dfac704069 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java @@ -0,0 +1,774 @@ +/* + * 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.vectorization; + +import java.util.Arrays; +import java.util.Random; +import jdk.incubator.vector.*; + +/** + * The code below is supposed to be an exact copy of: + * micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java + */ +public class VectorAlgorithmsImpl { + private static final VectorSpecies SPECIES_I = IntVector.SPECIES_PREFERRED; + private static final VectorSpecies SPECIES_I512 = IntVector.SPECIES_512; + private static final VectorSpecies SPECIES_I256 = IntVector.SPECIES_256; + private static final VectorSpecies SPECIES_B = ByteVector.SPECIES_PREFERRED; + private static final VectorSpecies SPECIES_B64 = ByteVector.SPECIES_64; + private static final VectorSpecies SPECIES_F = FloatVector.SPECIES_PREFERRED; + + // This class stores the input and output arrays. + // The constructor sets up all the data. + // + // IMPORTANT: + // If you want to use some array but do NOT modify it: just use it. + // If you want to use it and DO want to modify it: clone it. This + // ensures that each test gets a separate copy, and that when we + // capture the modified arrays they are different for every method + // and run. + // An alternative to cloning is to use different return arrays for + // different implementations of the same group, e.g. rI1, rI2, ... + // + public static class Data { + public int[] aI; + public int[] rI1; + public int[] rI2; + public int[] rI3; + public int[] rI4; + public int[] eI; + // The test has to use the same index into eI for all implementations. But in the + // benchmark, we'd like to use random indices, so we use the index to advance through + // the array. + public int eI_idx = 0; + + public float[] aF; + public float[] bF; + + public byte[] aB; + public byte[] strB; + public byte[] rB1; + public byte[] rB2; + public byte[] rB3; + + public int[] oopsX4; + public int[] memX4; + + public Data(int size, int seed, int numX4Objects) { + Random random = new Random(seed); + + // int: one input array and multiple output arrays so different implementations can + // store their results to different arrays. + aI = new int[size]; + rI1 = new int[size]; + rI2 = new int[size]; + rI3 = new int[size]; + rI4 = new int[size]; + Arrays.setAll(aI, i -> random.nextInt()); + + // Populate with some random values from aI, and some totally random values. + eI = new int[0x10000]; + for (int i = 0; i < eI.length; i++) { + eI[i] = (random.nextInt(10) == 0) ? random.nextInt() : aI[random.nextInt(size)]; + } + + // X4 oop setup. + // oopsX4 holds "addresses" (i.e. indices), that point to the 16-byte objects in memX4. + oopsX4 = new int[size]; + memX4 = new int[numX4Objects * 4]; + for (int i = 0; i < size; i++) { + // assign either a zero=null, or assign a random oop. + oopsX4[i] = (random.nextInt(10) == 0) ? 0 : random.nextInt(numX4Objects) * 4; + } + // Just fill the whole array with random values. + // The relevant field is only at every "4 * i + 3" though. + memX4 = new int[4 * numX4Objects]; + for (int i = 0; i < memX4.length; i++) { + memX4[i] = random.nextInt(); + } + + // float inputs. To avoid rounding issues, only use small integers. + aF = new float[size]; + bF = new float[size]; + for (int i = 0; i < size; i++) { + aF[i] = random.nextInt(32) - 16; + bF[i] = random.nextInt(32) - 16; + } + + // byte: just random data. + aB = new byte[size]; + strB = new byte[size]; + rB1 = new byte[size]; + rB2 = new byte[size]; + rB3 = new byte[size]; + random.nextBytes(aB); + random.nextBytes(strB); // TODO: special data! + } + } + + public static Object fillI_loop(int[] r) { + for (int i = 0; i < r.length; i++) { + r[i] = 42; + } + return r; + } + + public static Object fillI_Arrays(int[] r) { + Arrays.fill(r, 42); + return r; + } + + public static Object fillI_VectorAPI(int[] r) { + var v = IntVector.broadcast(SPECIES_I, 42); + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = 42; + } + return r; + } + + public static Object iotaI_loop(int[] r) { + for (int i = 0; i < r.length; i++) { + r[i] = i; + } + return r; + } + + public static Object iotaI_VectorAPI(int[] r) { + var iota = IntVector.broadcast(SPECIES_I, 0).addIndex(1); + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + iota.intoArray(r, i); + iota = iota.add(SPECIES_I.length()); + } + for (; i < r.length; i++) { + r[i] = i; + } + return r; + } + + public static Object copyI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static Object copyI_System_arraycopy(int[] a, int[] r) { + System.arraycopy(a, 0, r, 0, a.length); + return r; + } + + public static Object copyI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static Object mapI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[i] = a[i] * 42; + } + return r; + } + + public static Object mapI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v = v.mul(42); + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = a[i] * 42; + } + return r; + } + + public static int reduceAddI_loop(int[] a) { + int sum = 0; + for (int i = 0; i < a.length; i++) { + // Relying on simple reduction loop should vectorize since JDK26. + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_reassociate(int[] a) { + int sum = 0; + int i; + for (i = 0; i < a.length - 3; i += 4) { + // Unroll 4x, reassociate inside. + sum += a[i] + a[i + 1] + a[i + 2] + a[i + 3]; + } + for (; i < a.length; i++) { + // Tail + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_VectorAPI_naive(int[] a) { + var sum = 0; + int i; + for (i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + // reduceLanes in loop is better than scalar performance, but still + // relatively slow. + sum += v.reduceLanes(VectorOperators.ADD); + } + for (; i < a.length; i++) { + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_VectorAPI_reduction_after_loop(int[] a) { + var acc = IntVector.broadcast(SPECIES_I, 0); + int i; + for (i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + // Element-wide addition into a vector of partial sums is much faster. + // Now, we only need to do a reduceLanes after the loop. + // This works because int-addition is associative and commutative. + acc = acc.add(v); + } + int sum = acc.reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + sum += a[i]; + } + return sum; + } + + public static float dotProductF_loop(float[] a, float[] b) { + float sum = 0; + for (int i = 0; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static float dotProductF_VectorAPI_naive(float[] a, float[] b) { + float sum = 0; + int i; + for (i = 0; i < SPECIES_F.loopBound(a.length); i += SPECIES_F.length()) { + var va = FloatVector.fromArray(SPECIES_F, a, i); + var vb = FloatVector.fromArray(SPECIES_F, b, i); + sum += va.mul(vb).reduceLanes(VectorOperators.ADD); + } + for (; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static float dotProductF_VectorAPI_reduction_after_loop(float[] a, float[] b) { + var sums = FloatVector.broadcast(SPECIES_F, 0.0f); + int i; + for (i = 0; i < SPECIES_F.loopBound(a.length); i += SPECIES_F.length()) { + var va = FloatVector.fromArray(SPECIES_F, a, i); + var vb = FloatVector.fromArray(SPECIES_F, b, i); + sums = sums.add(va.mul(vb)); + } + float sum = sums.reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static int hashCodeB_loop(byte[] a) { + int h = 1; + for (int i = 0; i < a.length; i++) { + h = 31 * h + a[i]; + } + return h; + } + + public static int hashCodeB_Arrays(byte[] a) { + return Arrays.hashCode(a); + } + + // Simplified intrinsic code from C2_MacroAssembler::arrays_hashcode in c2_MacroAssembler_x86.cpp + // + // Ideas that may help understand the code: + // h(i) = 31 * h(i-1) + a[i] + // "unroll" by factor of L=8: + // h(i+8) = h(i) * 31^8 + a[i+1] * 31^7 + a[i+2] * 31^6 + ... + a[i+8] * 1 + // ----------- ------------------------------------------------ + // scalar vector: notice the powers of 31 in reverse + // + // We notice that we can load a[i+1 .. i+8], then element-wise multiply with + // the vector of reversed powers-of-31, and then do reduceLanes(ADD). + // But we can do even better: By looking at multiple such 8-unrolled iterations. + // Instead of applying the "next" factor of "31^8" to the reduced scalar, we can + // already apply it element-wise. That allows us to move the reduction out + // of the loop. + // + // Note: the intrinsic additionally unrolls the loop by a factor of 4, + // but we want to keep thins simple for demonstration purposes. + // + private static int[] REVERSE_POWERS_OF_31 = new int[9]; + static { + int p = 1; + for (int i = REVERSE_POWERS_OF_31.length - 1; i >= 0; i--) { + REVERSE_POWERS_OF_31[i] = p; + p *= 31; + } + } + public static int hashCodeB_VectorAPI_v1(byte[] a) { + int result = 1; // initialValue + var vresult = IntVector.zero(SPECIES_I256); + int next = REVERSE_POWERS_OF_31[0]; // 31^L + var vcoef = IntVector.fromArray(SPECIES_I256, REVERSE_POWERS_OF_31, 1); // powers of 2 in reverse + int i; + for (i = 0; i < SPECIES_B64.loopBound(a.length); i += SPECIES_B64.length()) { + // scalar part: result *= 31^L + result *= next; + // vector part: element-wise apply the next factor and add in the new values. + var vb = ByteVector.fromArray(SPECIES_B64, a, i); + var vi = vb.castShape(SPECIES_I256, 0); + vresult = vresult.mul(next).add(vi); + } + // reduce the partial hashes in the elements, using the reverse list of powers of 2. + result += vresult.mul(vcoef).reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + result = 31 * result + a[i]; + } + return result; + } + + // This second approach follows the idea from this blog post by Otmar Ertl: + // https://www.dynatrace.com/news/blog/java-arrays-hashcode-byte-efficiency-techniques/ + // + // I simplified the algorithm a little, so that it is a bit closer + // to the solution "v1" above. + // + // The major issue with "v1" is that we cannot load a full vector of bytes, + // because of the cast to ints. So we can only fill 1/4 of the maximal + // vector size. The trick here is to do an unrolling of factor 4, from: + // h(i) = 31 * h(i-1) + a[i] + // to: + // h(i+4) = h(i) * 31^4 + a[i + 1] * 31^3 + // + a[i + 2] * 31^2 + // + a[i + 3] * 31^1 + // + a[i + 4] * 31^0 + // The goal is now to compute this value for 4 bytes within a 4 byte + // lane of the vector. One concern is that we start with byte values, + // but need to do int-multiplication with powers of 31. If we instead + // did a byte-multiplication, we could get overflows that we would not + // have had in the int-multiplication. + // One trick that helps with chaning the size of the lanes from byte + // to short to int is doing all operations with unsigned integers. That + // way, we can zero-extend instead of sign-bit extend. The first step + // is thus to convert the bytes into unsigned values. Since byte is in + // range [-128..128), doing "a[i+j] + 128" makes it a positive value, + // allowing for unsigned multiplication. + // h(i+4) = h(i) * 31^4 + a[i + 1] * 31^3 + // + a[i + 2] * 31^2 + // + a[i + 3] * 31^1 + // + a[i + 4] * 31^0 + // = h(i) * 31^4 + (a[i + 1] + 128 - 128) * 31^3 + // + (a[i + 2] + 128 - 128) * 31^2 + // + (a[i + 3] + 128 - 128) * 31^1 + // + (a[i + 4] + 128 - 128) * 31^0 + // = h(i) * 31^4 + (a[i + 1] + 128 ) * 31^3 + // + (a[i + 2] + 128 ) * 31^2 + // + (a[i + 3] + 128 ) * 31^1 + // + (a[i + 4] + 128 ) * 31^0 + // + -128 * (31^3 + 31^2 + 31^1 + 1) + // = h(i) * 31^4 + ((a[i + 1] + 128) * 31 + // + (a[i + 2] + 128 ) * 31^2 + // + ((a[i + 3] + 128) * 31 + // + (a[i + 4] + 128 ) + // + -128 * (31^3 + 31^2 + 31^1 + 1) + // + // Getting from the signed a[i] value to unsigned with +128, we can + // just xor with 0x80=128. Any numbers there in range [-128..0) are + // now in range [0..128). And any numbers that were in range [0..128) + // are now in unsigned range [128..255). What a neat trick! + // + // We then apply a byte->short transition where we crunch 2 bytes + // into one short, applying a multiplication with 31 to one of the + // two bytes. This multiplication cannot overflow in a short. + // then we apply a short->int transition where we crunch 2 shorts + // into one int, applying a multiplication with 31^2 to one of the + // two shorts. This multiplication cannot overflow in an int. + // + public static int hashCodeB_VectorAPI_v2(byte[] a) { + return HashCodeB_VectorAPI_V2.compute(a); + } + + private static class HashCodeB_VectorAPI_V2 { + private static final int L = Math.min(ByteVector.SPECIES_PREFERRED.length(), + IntVector.SPECIES_PREFERRED.length() * 4); + private static final VectorShape SHAPE = VectorShape.forBitSize(8 * L); + private static final VectorSpecies SPECIES_B = SHAPE.withLanes(byte.class); + private static final VectorSpecies SPECIES_I = SHAPE.withLanes(int.class); + + private static int[] REVERSE_POWERS_OF_31_STEP_4 = new int[L / 4 + 1]; + static { + int p = 1; + int step = 31 * 31 * 31 * 31; // step by 4 + for (int i = REVERSE_POWERS_OF_31_STEP_4.length - 1; i >= 0; i--) { + REVERSE_POWERS_OF_31_STEP_4[i] = p; + p *= step; + } + } + + public static int compute(byte[] a) { + int result = 1; // initialValue + int next = REVERSE_POWERS_OF_31_STEP_4[0]; // 31^L + var vcoef = IntVector.fromArray(SPECIES_I, REVERSE_POWERS_OF_31_STEP_4, 1); // W + var vresult = IntVector.zero(SPECIES_I); + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vb = ByteVector.fromArray(SPECIES_B, a, i); + // Add 128 to each byte. + var vs = vb.lanewise(VectorOperators.XOR, (byte)0x80) + .reinterpretAsShorts(); + // Each short lane contains 2 bytes, crunch them. + var vi = vs.and((short)0xff) // lower byte + .mul((short)31) + .add(vs.lanewise(VectorOperators.LSHR, 8)) // upper byte + .reinterpretAsInts(); + // Each int contains 2 shorts, crunch them. + var v = vi.and(0xffff) // lower short + .mul(31 * 31) + .add(vi.lanewise(VectorOperators.LSHR, 16)); // upper short + // Add the correction for the 128 additions above. + v = v.add(-128 * (31*31*31 + 31*31 + 31 + 1)); + // Every element of v now contains a crunched int-package of 4 bytes. + result *= next; + vresult = vresult.mul(next).add(v); + } + result += vresult.mul(vcoef).reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + result = 31 * result + a[i]; + } + return result; + } + } + + public static Object scanAddI_loop(int[] a, int[] r) { + int sum = 0; + for (int i = 0; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static Object scanAddI_loop_reassociate(int[] a, int[] r) { + int sum = 0; + int i = 0; + for (; i < a.length - 3; i += 4) { + // We cut the latency by a factor of 4, but increase the number of additions. + int old_sum = sum; + int v0 = a[i + 0]; + int v1 = a[i + 1]; + int v2 = a[i + 2]; + int v3 = a[i + 3]; + int v01 = v0 + v1; + int v23 = v2 + v3; + int v0123 = v01 + v23; + sum += v0123; + r[i + 0] = old_sum + v0; + r[i + 1] = old_sum + v01; + r[i + 2] = old_sum + v01 + v2; + r[i + 3] = old_sum + v0123; + } + for (; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static Object scanAddI_VectorAPI_permute_add(int[] a, int[] r) { + // Using Naive Parallel Algorithm: Hills and Steele + int sum = 0; + int xx = 0; // masked later anyway + var shf1 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 0); + var shf2 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 0); + var shf3 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 0); + var shf4 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, xx, xx, xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7}, 0); + var mask1 = VectorMask.fromLong(SPECIES_I512, 0b1111111111111110); + var mask2 = VectorMask.fromLong(SPECIES_I512, 0b1111111111111100); + var mask3 = VectorMask.fromLong(SPECIES_I512, 0b1111111111110000); + var mask4 = VectorMask.fromLong(SPECIES_I512, 0b1111111100000000); + int i = 0; + for (; i < SPECIES_I512.loopBound(a.length); i += SPECIES_I512.length()) { + IntVector v = IntVector.fromArray(SPECIES_I512, a, i); + v = v.add(v.rearrange(shf1), mask1); + v = v.add(v.rearrange(shf2), mask2); + v = v.add(v.rearrange(shf3), mask3); + v = v.add(v.rearrange(shf4), mask4); + v = v.add(sum); + v.intoArray(r, i); + sum = v.lane(SPECIES_I512.length() - 1); + } + for (; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static int findMinIndexI_loop(int[] a) { + int min = a[0]; + int index = 0; + for (int i = 1; i < a.length; i++) { + int ai = a[i]; + if (ai < min) { + min = ai; + index = i; + } + } + return index; + } + + public static int findMinIndexI_VectorAPI(int[] a) { + // Main approach: have partial results in mins and idxs. + var mins = IntVector.broadcast(SPECIES_I, a[0]); + var idxs = IntVector.broadcast(SPECIES_I, 0); + var iota = IntVector.broadcast(SPECIES_I, 0).addIndex(1); + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.LT, mins); + mins = mins.blend(v, mask); + idxs = idxs.blend(iota, mask); + iota = iota.add(SPECIES_I.length()); + } + // Reduce the vectors down + int min = mins.reduceLanes(VectorOperators.MIN); + var not_min_mask = mins.compare(VectorOperators.NE, min); + int index = idxs.blend(a.length, not_min_mask).reduceLanes(VectorOperators.MIN); + // Tail loop + for (; i < a.length; i++) { + int ai = a[i]; + if (ai < min) { + min = ai; + index = i; + } + } + return index; + } + + public static int findI_loop(int[] a, int e) { + for (int i = 0; i < a.length; i++) { + int ai = a[i]; + if (ai == e) { + return i; + } + } + return -1; + } + + public static int findI_VectorAPI(int[] a, int e) { + var es = IntVector.broadcast(SPECIES_I, e); + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.EQ, es); + if (mask.anyTrue()) { + return i + mask.firstTrue(); + } + } + for (; i < a.length; i++) { + int ai = a[i]; + if (ai == e) { + return i; + } + } + return -1; + } + + public static Object reverseI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[a.length - i - 1] = a[i]; + } + return r; + } + + private static final VectorShuffle REVERSE_SHUFFLE_I = SPECIES_I.iotaShuffle(SPECIES_I.length()-1, -1, true); + + public static Object reverseI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v = v.rearrange(REVERSE_SHUFFLE_I); + v.intoArray(r, r.length - SPECIES_I.length() - i); + } + for (; i < a.length; i++) { + r[a.length - i - 1] = a[i]; + } + return r; + } + + public static Object filterI_loop(int[] a, int[] r, int threshold) { + int j = 0; + for (int i = 0; i < a.length; i++) { + int ai = a[i]; + if (ai >= threshold) { + r[j++] = ai; + } + } + // Just force the resulting length onto the same array. + r[r.length - 1] = j; + return r; + } + + public static Object filterI_VectorAPI(int[] a, int[] r, int threshold) { + var thresholds = IntVector.broadcast(SPECIES_I, threshold); + int j = 0; + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.GE, thresholds); + v = v.compress(mask); + int trueCount = mask.trueCount(); + var prefixMask = mask.compress(); + v.intoArray(r, j, prefixMask); + j += trueCount; + } + + for (; i < a.length; i++) { + int ai = a[i]; + if (ai >= threshold) { + r[j++] = ai; + } + } + // Just force the resulting length onto the same array. + r[r.length - 1] = j; + return r; + } + + // X4: ints simulate 4-byte oops. + // oops: if non-zero (= non-null), every entry simulates a 4-byte oop, pointing into mem. + // mem: an int array that simulates the memory. + // + // Task: Find all non-null oops, and dereference them, get the relevant field. + // Objects have 16 bytes, and the relevant field is at bytes 12-16. + // That maps to 4 ints, and the relevant field is the 4th element of 4. + // Sum up all the field values. + public static int reduceAddIFieldsX4_loop(int[] oops, int[] mem) { + int sum = 0; + for (int i = 0; i < oops.length; i++) { + int oop = oops[i]; + if (oop != 0) { + int fieldValue = mem[oop + 3]; // oop+12 + sum += fieldValue; + } + } + return sum; + } + + public static int reduceAddIFieldsX4_VectorAPI(int[] oops, int[] mem) { + var acc = IntVector.broadcast(SPECIES_I, 0); + int i = 0; + for (; i < SPECIES_I.loopBound(oops.length); i += SPECIES_I.length()) { + var oopv = IntVector.fromArray(SPECIES_I, oops, i); + var mask = oopv.compare(VectorOperators.NE, /* null */0); + // We are lucky today: we need to access mem[oop + 3] + var fieldValues = IntVector.fromArray(SPECIES_I, mem, 3, oops, i, mask); + acc = acc.add(fieldValues); + } + int sum = acc.reduceLanes(VectorOperators.ADD); + for (; i < oops.length; i++) { + int oop = oops[i]; + if (oop != 0) { + int fieldValue = mem[oop + 3]; // oop+12 + sum += fieldValue; + } + } + return sum; + } + + // The lowerCase example demonstrates a lane-wise control-flow diamond. + public static Object lowerCaseB_loop(byte[] a, byte[] r) { + for (int i = 0; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); // c += 32 + } + r[i] = c; + } + return r; + } + + // Control-flow diamonds can easily be simulated by "if-conversion", i.e. + // by using masked operations. An alternative would be to use blend. + public static Object lowerCaseB_VectorAPI_v1(byte[] a, byte[] r) { + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vc = ByteVector.fromArray(SPECIES_B, a, i); + var maskA = vc.compare(VectorOperators.GE, (byte)'A'); + var maskZ = vc.compare(VectorOperators.LE, (byte)'Z'); + var mask = maskA.and(maskZ); + vc = vc.add((byte)32, mask); + vc.intoArray(r, i); + } + for (; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + } + r[i] = c; + } + return r; + } + + public static Object lowerCaseB_VectorAPI_v2(byte[] a, byte[] r) { + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vc = ByteVector.fromArray(SPECIES_B, a, i); + // We can convert the range 65..90 (represents ascii A..Z) into a range 0..25. + // This allows us to only use a single unsigned comparison. + var vt = vc.add((byte)-'A'); + var mask = vt.compare(VectorOperators.ULE, (byte)25); + vc = vc.add((byte)32, mask); + vc.intoArray(r, i); + } + for (; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + } + r[i] = c; + } + return r; + } +} diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java new file mode 100644 index 00000000000..911494cb022 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithms.java @@ -0,0 +1,274 @@ +/* + * 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 org.openjdk.bench.vm.compiler; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; + +/** + * The goal of this benchmark is to show the power of auto vectorization + * and the Vector API. + * + * Please only modify this benchark in synchronization with the IR test: + * test/hotspot/jtreg/compiler/vectorization/TestVectorAlgorithms.java + * + * You may want to play with the following VM flags: + * - Disable auto vectorization: + * -XX:+UnlockDiagnosticVMOptions -XX:AutoVectorizationOverrideProfitability=0 + * - Smaller vector size: + * -XX:MaxVectorSize=16 + * - Disable fill loop detection, so we don't use intrinsic but auto vectorization: + * -XX:-OptimizeFill + * - Lilliput can also have an effect, because it can change alignment and have + * an impact on which exact intrinsic is chosen (e.g. fill and copy): + * -XX:+UseCompactObjectHeaders + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 3, time = 1) +@Fork(value = 5, jvmArgs = {"--add-modules=jdk.incubator.vector", "-XX:CompileCommand=inline,*VectorAlgorithmsImpl*::*"}) +public class VectorAlgorithms { + @Param({"640000"}) + public int SIZE; + + @Param({"10000"}) + public int NUM_X_OBJECTS; + + @Param({"0"}) + public int SEED; + + VectorAlgorithmsImpl.Data d; + + @Setup + public void init() { + d = new VectorAlgorithmsImpl.Data(SIZE, SEED, NUM_X_OBJECTS); + } + + // ------------------------------------------------------------------------------------------ + // Benchmarks just forward arguments and returns. + // ------------------------------------------------------------------------------------------ + + @Benchmark + public Object fillI_loop() { + return VectorAlgorithmsImpl.fillI_loop(d.rI1); + } + + @Benchmark + public Object fillI_VectorAPI() { + return VectorAlgorithmsImpl.fillI_VectorAPI(d.rI1); + } + + @Benchmark + public Object fillI_Arrays() { + return VectorAlgorithmsImpl.fillI_Arrays(d.rI1); + } + + @Benchmark + public Object iotaI_loop() { + return VectorAlgorithmsImpl.iotaI_loop(d.rI1); + } + + @Benchmark + public Object iotaI_VectorAPI() { + return VectorAlgorithmsImpl.iotaI_VectorAPI(d.rI1); + } + + @Benchmark + public Object copyI_loop() { + return VectorAlgorithmsImpl.copyI_loop(d.aI, d.rI1); + } + + @Benchmark + public Object copyI_VectorAPI() { + return VectorAlgorithmsImpl.copyI_VectorAPI(d.aI, d.rI1); + } + + @Benchmark + public Object copyI_System_arraycopy() { + return VectorAlgorithmsImpl.copyI_System_arraycopy(d.aI, d.rI1); + } + + @Benchmark + public Object mapI_loop() { + return VectorAlgorithmsImpl.mapI_loop(d.aI, d.rI1); + } + + @Benchmark + public Object mapI_VectorAPI() { + return VectorAlgorithmsImpl.mapI_VectorAPI(d.aI, d.rI1); + } + + @Benchmark + public int reduceAddI_loop() { + return VectorAlgorithmsImpl.reduceAddI_loop(d.aI); + } + + @Benchmark + public int reduceAddI_reassociate() { + return VectorAlgorithmsImpl.reduceAddI_reassociate(d.aI); + } + + @Benchmark + public int reduceAddI_VectorAPI_naive() { + return VectorAlgorithmsImpl.reduceAddI_VectorAPI_naive(d.aI); + } + + @Benchmark + public int reduceAddI_VectorAPI_reduction_after_loop() { + return VectorAlgorithmsImpl.reduceAddI_VectorAPI_reduction_after_loop(d.aI); + } + + @Benchmark + public float dotProductF_loop() { + return VectorAlgorithmsImpl.dotProductF_loop(d.aF, d.bF); + } + + @Benchmark + public float dotProductF_VectorAPI_naive() { + return VectorAlgorithmsImpl.dotProductF_VectorAPI_naive(d.aF, d.bF); + } + + @Benchmark + public float dotProductF_VectorAPI_reduction_after_loop() { + return VectorAlgorithmsImpl.dotProductF_VectorAPI_reduction_after_loop(d.aF, d.bF); + } + + @Benchmark + public int hashCodeB_loop() { + return VectorAlgorithmsImpl.hashCodeB_loop(d.aB); + } + + @Benchmark + public int hashCodeB_Arrays() { + return VectorAlgorithmsImpl.hashCodeB_Arrays(d.aB); + } + + @Benchmark + public int hashCodeB_VectorAPI_v1() { + return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v1(d.aB); + } + + @Benchmark + public int hashCodeB_VectorAPI_v2() { + return VectorAlgorithmsImpl.hashCodeB_VectorAPI_v2(d.aB); + } + + @Benchmark + public Object scanAddI_loop() { + return VectorAlgorithmsImpl.scanAddI_loop(d.aI, d.rI1); + } + + @Benchmark + public Object scanAddI_loop_reassociate() { + return VectorAlgorithmsImpl.scanAddI_loop_reassociate(d.aI, d.rI1); + } + + @Benchmark + public Object scanAddI_VectorAPI_permute_add() { + return VectorAlgorithmsImpl.scanAddI_VectorAPI_permute_add(d.aI, d.rI1); + } + + @Benchmark + public int findMinIndexI_loop() { + return VectorAlgorithmsImpl.findMinIndexI_loop(d.aI); + } + + @Benchmark + public int findMinIndexI_VectorAPI() { + return VectorAlgorithmsImpl.findMinIndexI_VectorAPI(d.aI); + } + + @Benchmark + public int findI_loop() { + // Every invocation should have a different value for e, so that + // we don't get branch-prediction that is too good. And also so + // that the position where we exit is more evenly distributed. + d.eI_idx = (d.eI_idx + 1) & 0xffff; + int e = d.eI[d.eI_idx]; + return VectorAlgorithmsImpl.findI_loop(d.aI, e); + } + + @Benchmark + public int findI_VectorAPI() { + d.eI_idx = (d.eI_idx + 1) & 0xffff; + int e = d.eI[d.eI_idx]; + return VectorAlgorithmsImpl.findI_VectorAPI(d.aI, e); + } + + @Benchmark + public Object reverseI_loop() { + return VectorAlgorithmsImpl.reverseI_loop(d.aI, d.rI1); + } + + @Benchmark + public Object reverseI_VectorAPI() { + return VectorAlgorithmsImpl.reverseI_VectorAPI(d.aI, d.rI1); + } + + @Benchmark + public Object filterI_loop() { + // Every invocation should have a different value for e, so that + // we don't get branch-prediction that is too good. And also so + // That the length of the resulting data is more evenly distributed. + d.eI_idx = (d.eI_idx + 1) & 0xffff; + int e = d.eI[d.eI_idx]; + return VectorAlgorithmsImpl.filterI_loop(d.aI, d.rI1, e); + } + + @Benchmark + public Object filterI_VectorAPI() { + d.eI_idx = (d.eI_idx + 1) & 0xffff; + int e = d.eI[d.eI_idx]; + return VectorAlgorithmsImpl.filterI_VectorAPI(d.aI, d.rI1, e); + } + + @Benchmark + public int reduceAddIFieldsX4_loop() { + return VectorAlgorithmsImpl.reduceAddIFieldsX4_loop(d.oopsX4, d.memX4); + } + + @Benchmark + public int reduceAddIFieldsX4_VectorAPI() { + return VectorAlgorithmsImpl.reduceAddIFieldsX4_VectorAPI(d.oopsX4, d.memX4); + } + + @Benchmark + public Object lowerCaseB_loop() { + return VectorAlgorithmsImpl.lowerCaseB_loop(d.strB, d.rB1); + } + + @Benchmark + public Object lowerCaseB_VectorAPI_v1() { + return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v1(d.strB, d.rB1); + } + + @Benchmark + public Object lowerCaseB_VectorAPI_v2() { + return VectorAlgorithmsImpl.lowerCaseB_VectorAPI_v2(d.strB, d.rB1); + } +} diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java new file mode 100644 index 00000000000..5ab057329d3 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorAlgorithmsImpl.java @@ -0,0 +1,775 @@ +/* + * 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 org.openjdk.bench.vm.compiler; + +import java.util.Arrays; +import java.util.Random; +import jdk.incubator.vector.*; + +/** + * The code below is supposed to be an exact copy of: + * test/hotspot/jtreg/compiler/vectorization/VectorAlgorithmsImpl.java + */ +public class VectorAlgorithmsImpl { + private static final VectorSpecies SPECIES_I = IntVector.SPECIES_PREFERRED; + private static final VectorSpecies SPECIES_I512 = IntVector.SPECIES_512; + private static final VectorSpecies SPECIES_I256 = IntVector.SPECIES_256; + private static final VectorSpecies SPECIES_B = ByteVector.SPECIES_PREFERRED; + private static final VectorSpecies SPECIES_B64 = ByteVector.SPECIES_64; + private static final VectorSpecies SPECIES_F = FloatVector.SPECIES_PREFERRED; + + // This class stores the input and output arrays. + // The constructor sets up all the data. + // + // IMPORTANT: + // If you want to use some array but do NOT modify it: just use it. + // If you want to use it and DO want to modify it: clone it. This + // ensures that each test gets a separate copy, and that when we + // capture the modified arrays they are different for every method + // and run. + // An alternative to cloning is to use different return arrays for + // different implementations of the same group, e.g. rI1, rI2, ... + // + public static class Data { + public int[] aI; + public int[] rI1; + public int[] rI2; + public int[] rI3; + public int[] rI4; + public int[] eI; + // The test has to use the same index into eI for all implementations. But in the + // benchmark, we'd like to use random indices, so we use the index to advance through + // the array. + public int eI_idx = 0; + + public float[] aF; + public float[] bF; + + public byte[] aB; + public byte[] strB; + public byte[] rB1; + public byte[] rB2; + public byte[] rB3; + + public int[] oopsX4; + public int[] memX4; + + public Data(int size, int seed, int numX4Objects) { + Random random = new Random(seed); + + // int: one input array and multiple output arrays so different implementations can + // store their results to different arrays. + aI = new int[size]; + rI1 = new int[size]; + rI2 = new int[size]; + rI3 = new int[size]; + rI4 = new int[size]; + Arrays.setAll(aI, i -> random.nextInt()); + + // Populate with some random values from aI, and some totally random values. + eI = new int[0x10000]; + for (int i = 0; i < eI.length; i++) { + eI[i] = (random.nextInt(10) == 0) ? random.nextInt() : aI[random.nextInt(size)]; + } + + // X4 oop setup. + // oopsX4 holds "addresses" (i.e. indices), that point to the 16-byte objects in memX4. + oopsX4 = new int[size]; + memX4 = new int[numX4Objects * 4]; + for (int i = 0; i < size; i++) { + // assign either a zero=null, or assign a random oop. + oopsX4[i] = (random.nextInt(10) == 0) ? 0 : random.nextInt(numX4Objects) * 4; + } + // Just fill the whole array with random values. + // The relevant field is only at every "4 * i + 3" though. + memX4 = new int[4 * numX4Objects]; + for (int i = 0; i < memX4.length; i++) { + memX4[i] = random.nextInt(); + } + + // float inputs. To avoid rounding issues, only use small integers. + aF = new float[size]; + bF = new float[size]; + for (int i = 0; i < size; i++) { + aF[i] = random.nextInt(32) - 16; + bF[i] = random.nextInt(32) - 16; + } + + // byte: just random data. + aB = new byte[size]; + strB = new byte[size]; + rB1 = new byte[size]; + rB2 = new byte[size]; + rB3 = new byte[size]; + random.nextBytes(aB); + random.nextBytes(strB); // TODO: special data! + } + } + + public static Object fillI_loop(int[] r) { + for (int i = 0; i < r.length; i++) { + r[i] = 42; + } + return r; + } + + public static Object fillI_Arrays(int[] r) { + Arrays.fill(r, 42); + return r; + } + + public static Object fillI_VectorAPI(int[] r) { + var v = IntVector.broadcast(SPECIES_I, 42); + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = 42; + } + return r; + } + + public static Object iotaI_loop(int[] r) { + for (int i = 0; i < r.length; i++) { + r[i] = i; + } + return r; + } + + public static Object iotaI_VectorAPI(int[] r) { + var iota = IntVector.broadcast(SPECIES_I, 0).addIndex(1); + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + iota.intoArray(r, i); + iota = iota.add(SPECIES_I.length()); + } + for (; i < r.length; i++) { + r[i] = i; + } + return r; + } + + public static Object copyI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static Object copyI_System_arraycopy(int[] a, int[] r) { + System.arraycopy(a, 0, r, 0, a.length); + return r; + } + + public static Object copyI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = a[i]; + } + return r; + } + + public static Object mapI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[i] = a[i] * 42; + } + return r; + } + + public static Object mapI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(r.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v = v.mul(42); + v.intoArray(r, i); + } + for (; i < r.length; i++) { + r[i] = a[i] * 42; + } + return r; + } + + public static int reduceAddI_loop(int[] a) { + int sum = 0; + for (int i = 0; i < a.length; i++) { + // Relying on simple reduction loop should vectorize since JDK26. + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_reassociate(int[] a) { + int sum = 0; + int i; + for (i = 0; i < a.length - 3; i += 4) { + // Unroll 4x, reassociate inside. + sum += a[i] + a[i + 1] + a[i + 2] + a[i + 3]; + } + for (; i < a.length; i++) { + // Tail + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_VectorAPI_naive(int[] a) { + var sum = 0; + int i; + for (i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + // reduceLanes in loop is better than scalar performance, but still + // relatively slow. + sum += v.reduceLanes(VectorOperators.ADD); + } + for (; i < a.length; i++) { + sum += a[i]; + } + return sum; + } + + public static int reduceAddI_VectorAPI_reduction_after_loop(int[] a) { + var acc = IntVector.broadcast(SPECIES_I, 0); + int i; + for (i = 0; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + // Element-wide addition into a vector of partial sums is much faster. + // Now, we only need to do a reduceLanes after the loop. + // This works because int-addition is associative and commutative. + acc = acc.add(v); + } + int sum = acc.reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + sum += a[i]; + } + return sum; + } + + public static float dotProductF_loop(float[] a, float[] b) { + float sum = 0; + for (int i = 0; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static float dotProductF_VectorAPI_naive(float[] a, float[] b) { + float sum = 0; + int i; + for (i = 0; i < SPECIES_F.loopBound(a.length); i += SPECIES_F.length()) { + var va = FloatVector.fromArray(SPECIES_F, a, i); + var vb = FloatVector.fromArray(SPECIES_F, b, i); + sum += va.mul(vb).reduceLanes(VectorOperators.ADD); + } + for (; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static float dotProductF_VectorAPI_reduction_after_loop(float[] a, float[] b) { + var sums = FloatVector.broadcast(SPECIES_F, 0.0f); + int i; + for (i = 0; i < SPECIES_F.loopBound(a.length); i += SPECIES_F.length()) { + var va = FloatVector.fromArray(SPECIES_F, a, i); + var vb = FloatVector.fromArray(SPECIES_F, b, i); + sums = sums.add(va.mul(vb)); + } + float sum = sums.reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + sum += a[i] * b[i]; + } + return sum; + } + + public static int hashCodeB_loop(byte[] a) { + int h = 1; + for (int i = 0; i < a.length; i++) { + h = 31 * h + a[i]; + } + return h; + } + + public static int hashCodeB_Arrays(byte[] a) { + return Arrays.hashCode(a); + } + + // Simplified intrinsic code from C2_MacroAssembler::arrays_hashcode in c2_MacroAssembler_x86.cpp + // + // Ideas that may help understand the code: + // h(i) = 31 * h(i-1) + a[i] + // "unroll" by factor of L=8: + // h(i+8) = h(i) * 31^8 + a[i+1] * 31^7 + a[i+2] * 31^6 + ... + a[i+8] * 1 + // ----------- ------------------------------------------------ + // scalar vector: notice the powers of 31 in reverse + // + // We notice that we can load a[i+1 .. i+8], then element-wise multiply with + // the vector of reversed powers-of-31, and then do reduceLanes(ADD). + // But we can do even better: By looking at multiple such 8-unrolled iterations. + // Instead of applying the "next" factor of "31^8" to the reduced scalar, we can + // already apply it element-wise. That allows us to move the reduction out + // of the loop. + // + // Note: the intrinsic additionally unrolls the loop by a factor of 4, + // but we want to keep thins simple for demonstration purposes. + // + private static int[] REVERSE_POWERS_OF_31 = new int[9]; + static { + int p = 1; + for (int i = REVERSE_POWERS_OF_31.length - 1; i >= 0; i--) { + REVERSE_POWERS_OF_31[i] = p; + p *= 31; + } + } + public static int hashCodeB_VectorAPI_v1(byte[] a) { + int result = 1; // initialValue + var vresult = IntVector.zero(SPECIES_I256); + int next = REVERSE_POWERS_OF_31[0]; // 31^L + var vcoef = IntVector.fromArray(SPECIES_I256, REVERSE_POWERS_OF_31, 1); // powers of 2 in reverse + int i; + for (i = 0; i < SPECIES_B64.loopBound(a.length); i += SPECIES_B64.length()) { + // scalar part: result *= 31^L + result *= next; + // vector part: element-wise apply the next factor and add in the new values. + var vb = ByteVector.fromArray(SPECIES_B64, a, i); + var vi = vb.castShape(SPECIES_I256, 0); + vresult = vresult.mul(next).add(vi); + } + // reduce the partial hashes in the elements, using the reverse list of powers of 2. + result += vresult.mul(vcoef).reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + result = 31 * result + a[i]; + } + return result; + } + + // This second approach follows the idea from this blog post by Otmar Ertl: + // https://www.dynatrace.com/news/blog/java-arrays-hashcode-byte-efficiency-techniques/ + // + // I simplified the algorithm a little, so that it is a bit closer + // to the solution "v1" above. + // + // The major issue with "v1" is that we cannot load a full vector of bytes, + // because of the cast to ints. So we can only fill 1/4 of the maximal + // vector size. The trick here is to do an unrolling of factor 4, from: + // h(i) = 31 * h(i-1) + a[i] + // to: + // h(i+4) = h(i) * 31^4 + a[i + 1] * 31^3 + // + a[i + 2] * 31^2 + // + a[i + 3] * 31^1 + // + a[i + 4] * 31^0 + // The goal is now to compute this value for 4 bytes within a 4 byte + // lane of the vector. One concern is that we start with byte values, + // but need to do int-multiplication with powers of 31. If we instead + // did a byte-multiplication, we could get overflows that we would not + // have had in the int-multiplication. + // One trick that helps with chaning the size of the lanes from byte + // to short to int is doing all operations with unsigned integers. That + // way, we can zero-extend instead of sign-bit extend. The first step + // is thus to convert the bytes into unsigned values. Since byte is in + // range [-128..128), doing "a[i+j] + 128" makes it a positive value, + // allowing for unsigned multiplication. + // h(i+4) = h(i) * 31^4 + a[i + 1] * 31^3 + // + a[i + 2] * 31^2 + // + a[i + 3] * 31^1 + // + a[i + 4] * 31^0 + // = h(i) * 31^4 + (a[i + 1] + 128 - 128) * 31^3 + // + (a[i + 2] + 128 - 128) * 31^2 + // + (a[i + 3] + 128 - 128) * 31^1 + // + (a[i + 4] + 128 - 128) * 31^0 + // = h(i) * 31^4 + (a[i + 1] + 128 ) * 31^3 + // + (a[i + 2] + 128 ) * 31^2 + // + (a[i + 3] + 128 ) * 31^1 + // + (a[i + 4] + 128 ) * 31^0 + // + -128 * (31^3 + 31^2 + 31^1 + 1) + // = h(i) * 31^4 + ((a[i + 1] + 128) * 31 + // + (a[i + 2] + 128 ) * 31^2 + // + ((a[i + 3] + 128) * 31 + // + (a[i + 4] + 128 ) + // + -128 * (31^3 + 31^2 + 31^1 + 1) + // + // Getting from the signed a[i] value to unsigned with +128, we can + // just xor with 0x80=128. Any numbers there in range [-128..0) are + // now in range [0..128). And any numbers that were in range [0..128) + // are now in unsigned range [128..255). What a neat trick! + // + // We then apply a byte->short transition where we crunch 2 bytes + // into one short, applying a multiplication with 31 to one of the + // two bytes. This multiplication cannot overflow in a short. + // then we apply a short->int transition where we crunch 2 shorts + // into one int, applying a multiplication with 31^2 to one of the + // two shorts. This multiplication cannot overflow in an int. + // + public static int hashCodeB_VectorAPI_v2(byte[] a) { + return HashCodeB_VectorAPI_V2.compute(a); + } + + private static class HashCodeB_VectorAPI_V2 { + private static final int L = Math.min(ByteVector.SPECIES_PREFERRED.length(), + IntVector.SPECIES_PREFERRED.length() * 4); + private static final VectorShape SHAPE = VectorShape.forBitSize(8 * L); + private static final VectorSpecies SPECIES_B = SHAPE.withLanes(byte.class); + private static final VectorSpecies SPECIES_I = SHAPE.withLanes(int.class); + + private static int[] REVERSE_POWERS_OF_31_STEP_4 = new int[L / 4 + 1]; + static { + int p = 1; + int step = 31 * 31 * 31 * 31; // step by 4 + for (int i = REVERSE_POWERS_OF_31_STEP_4.length - 1; i >= 0; i--) { + REVERSE_POWERS_OF_31_STEP_4[i] = p; + p *= step; + } + } + + public static int compute(byte[] a) { + int result = 1; // initialValue + int next = REVERSE_POWERS_OF_31_STEP_4[0]; // 31^L + var vcoef = IntVector.fromArray(SPECIES_I, REVERSE_POWERS_OF_31_STEP_4, 1); // W + var vresult = IntVector.zero(SPECIES_I); + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vb = ByteVector.fromArray(SPECIES_B, a, i); + // Add 128 to each byte. + var vs = vb.lanewise(VectorOperators.XOR, (byte)0x80) + .reinterpretAsShorts(); + // Each short lane contains 2 bytes, crunch them. + var vi = vs.and((short)0xff) // lower byte + .mul((short)31) + .add(vs.lanewise(VectorOperators.LSHR, 8)) // upper byte + .reinterpretAsInts(); + // Each int contains 2 shorts, crunch them. + var v = vi.and(0xffff) // lower short + .mul(31 * 31) + .add(vi.lanewise(VectorOperators.LSHR, 16)); // upper short + // Add the correction for the 128 additions above. + v = v.add(-128 * (31*31*31 + 31*31 + 31 + 1)); + // Every element of v now contains a crunched int-package of 4 bytes. + result *= next; + vresult = vresult.mul(next).add(v); + } + result += vresult.mul(vcoef).reduceLanes(VectorOperators.ADD); + for (; i < a.length; i++) { + result = 31 * result + a[i]; + } + return result; + } + } + + public static Object scanAddI_loop(int[] a, int[] r) { + int sum = 0; + for (int i = 0; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static Object scanAddI_loop_reassociate(int[] a, int[] r) { + int sum = 0; + int i = 0; + for (; i < a.length - 3; i += 4) { + // We cut the latency by a factor of 4, but increase the number of additions. + int old_sum = sum; + int v0 = a[i + 0]; + int v1 = a[i + 1]; + int v2 = a[i + 2]; + int v3 = a[i + 3]; + int v01 = v0 + v1; + int v23 = v2 + v3; + int v0123 = v01 + v23; + sum += v0123; + r[i + 0] = old_sum + v0; + r[i + 1] = old_sum + v01; + r[i + 2] = old_sum + v01 + v2; + r[i + 3] = old_sum + v0123; + } + for (; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static Object scanAddI_VectorAPI_permute_add(int[] a, int[] r) { + // Using Naive Parallel Algorithm: Hills and Steele + int sum = 0; + int xx = 0; // masked later anyway + var shf1 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 0); + var shf2 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 0); + var shf3 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 0); + var shf4 = VectorShuffle.fromArray(SPECIES_I512, new int[]{xx, xx, xx, xx, xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7}, 0); + var mask1 = VectorMask.fromLong(SPECIES_I512, 0b1111111111111110); + var mask2 = VectorMask.fromLong(SPECIES_I512, 0b1111111111111100); + var mask3 = VectorMask.fromLong(SPECIES_I512, 0b1111111111110000); + var mask4 = VectorMask.fromLong(SPECIES_I512, 0b1111111100000000); + int i = 0; + for (; i < SPECIES_I512.loopBound(a.length); i += SPECIES_I512.length()) { + IntVector v = IntVector.fromArray(SPECIES_I512, a, i); + v = v.add(v.rearrange(shf1), mask1); + v = v.add(v.rearrange(shf2), mask2); + v = v.add(v.rearrange(shf3), mask3); + v = v.add(v.rearrange(shf4), mask4); + v = v.add(sum); + v.intoArray(r, i); + sum = v.lane(SPECIES_I512.length() - 1); + } + for (; i < a.length; i++) { + sum += a[i]; + r[i] = sum; + } + return r; + } + + public static int findMinIndexI_loop(int[] a) { + int min = a[0]; + int index = 0; + for (int i = 1; i < a.length; i++) { + int ai = a[i]; + if (ai < min) { + min = ai; + index = i; + } + } + return index; + } + + public static int findMinIndexI_VectorAPI(int[] a) { + // Main approach: have partial results in mins and idxs. + var mins = IntVector.broadcast(SPECIES_I, a[0]); + var idxs = IntVector.broadcast(SPECIES_I, 0); + var iota = IntVector.broadcast(SPECIES_I, 0).addIndex(1); + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.LT, mins); + mins = mins.blend(v, mask); + idxs = idxs.blend(iota, mask); + iota = iota.add(SPECIES_I.length()); + } + // Reduce the vectors down + int min = mins.reduceLanes(VectorOperators.MIN); + var not_min_mask = mins.compare(VectorOperators.NE, min); + int index = idxs.blend(a.length, not_min_mask).reduceLanes(VectorOperators.MIN); + // Tail loop + for (; i < a.length; i++) { + int ai = a[i]; + if (ai < min) { + min = ai; + index = i; + } + } + return index; + } + + public static int findI_loop(int[] a, int e) { + for (int i = 0; i < a.length; i++) { + int ai = a[i]; + if (ai == e) { + return i; + } + } + return -1; + } + + public static int findI_VectorAPI(int[] a, int e) { + var es = IntVector.broadcast(SPECIES_I, e); + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.EQ, es); + if (mask.anyTrue()) { + return i + mask.firstTrue(); + } + } + for (; i < a.length; i++) { + int ai = a[i]; + if (ai == e) { + return i; + } + } + return -1; + } + + public static Object reverseI_loop(int[] a, int[] r) { + for (int i = 0; i < a.length; i++) { + r[a.length - i - 1] = a[i]; + } + return r; + } + + private static final VectorShuffle REVERSE_SHUFFLE_I = SPECIES_I.iotaShuffle(SPECIES_I.length()-1, -1, true); + + public static Object reverseI_VectorAPI(int[] a, int[] r) { + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + v = v.rearrange(REVERSE_SHUFFLE_I); + v.intoArray(r, r.length - SPECIES_I.length() - i); + } + for (; i < a.length; i++) { + r[a.length - i - 1] = a[i]; + } + return r; + } + + public static Object filterI_loop(int[] a, int[] r, int threshold) { + int j = 0; + for (int i = 0; i < a.length; i++) { + int ai = a[i]; + if (ai >= threshold) { + r[j++] = ai; + } + } + // Just force the resulting length onto the same array. + r[r.length - 1] = j; + return r; + } + + public static Object filterI_VectorAPI(int[] a, int[] r, int threshold) { + var thresholds = IntVector.broadcast(SPECIES_I, threshold); + int j = 0; + int i = 0; + for (; i < SPECIES_I.loopBound(a.length); i += SPECIES_I.length()) { + IntVector v = IntVector.fromArray(SPECIES_I, a, i); + var mask = v.compare(VectorOperators.GE, thresholds); + v = v.compress(mask); + int trueCount = mask.trueCount(); + var prefixMask = mask.compress(); + v.intoArray(r, j, prefixMask); + j += trueCount; + } + + for (; i < a.length; i++) { + int ai = a[i]; + if (ai >= threshold) { + r[j++] = ai; + } + } + // Just force the resulting length onto the same array. + r[r.length - 1] = j; + return r; + } + + // X4: ints simulate 4-byte oops. + // oops: if non-zero (= non-null), every entry simulates a 4-byte oop, pointing into mem. + // mem: an int array that simulates the memory. + // + // Task: Find all non-null oops, and dereference them, get the relevant field. + // Objects have 16 bytes, and the relevant field is at bytes 12-16. + // That maps to 4 ints, and the relevant field is the 4th element of 4. + // Sum up all the field values. + public static int reduceAddIFieldsX4_loop(int[] oops, int[] mem) { + int sum = 0; + for (int i = 0; i < oops.length; i++) { + int oop = oops[i]; + if (oop != 0) { + int fieldValue = mem[oop + 3]; // oop+12 + sum += fieldValue; + } + } + return sum; + } + + public static int reduceAddIFieldsX4_VectorAPI(int[] oops, int[] mem) { + var acc = IntVector.broadcast(SPECIES_I, 0); + int i = 0; + for (; i < SPECIES_I.loopBound(oops.length); i += SPECIES_I.length()) { + var oopv = IntVector.fromArray(SPECIES_I, oops, i); + var mask = oopv.compare(VectorOperators.NE, /* null */0); + // We are lucky today: we need to access mem[oop + 3] + var fieldValues = IntVector.fromArray(SPECIES_I, mem, 3, oops, i, mask); + acc = acc.add(fieldValues); + } + int sum = acc.reduceLanes(VectorOperators.ADD); + for (; i < oops.length; i++) { + int oop = oops[i]; + if (oop != 0) { + int fieldValue = mem[oop + 3]; // oop+12 + sum += fieldValue; + } + } + return sum; + } + + // The lowerCase example demonstrates a lane-wise control-flow diamond. + public static Object lowerCaseB_loop(byte[] a, byte[] r) { + for (int i = 0; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); // c += 32 + } + r[i] = c; + } + return r; + } + + // Control-flow diamonds can easily be simulated by "if-conversion", i.e. + // by using masked operations. An alternative would be to use blend. + public static Object lowerCaseB_VectorAPI_v1(byte[] a, byte[] r) { + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vc = ByteVector.fromArray(SPECIES_B, a, i); + var maskA = vc.compare(VectorOperators.GE, (byte)'A'); + var maskZ = vc.compare(VectorOperators.LE, (byte)'Z'); + var mask = maskA.and(maskZ); + vc = vc.add((byte)32, mask); + vc.intoArray(r, i); + } + for (; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + } + r[i] = c; + } + return r; + } + + public static Object lowerCaseB_VectorAPI_v2(byte[] a, byte[] r) { + int i; + for (i = 0; i < SPECIES_B.loopBound(a.length); i += SPECIES_B.length()) { + var vc = ByteVector.fromArray(SPECIES_B, a, i); + // We can convert the range 65..90 (represents ascii A..Z) into a range 0..25. + // This allows us to only use a single unsigned comparison. + var vt = vc.add((byte)-'A'); + var mask = vt.compare(VectorOperators.ULE, (byte)25); + vc = vc.add((byte)32, mask); + vc.intoArray(r, i); + } + for (; i < a.length; i++) { + byte c = a[i]; + if (c >= 'A' && c <= 'Z') { + c += ('a' - 'A'); + } + r[i] = c; + } + return r; + } +} + From 92072a93bfeb83186df15032d425ed984d24fc52 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Thu, 29 Jan 2026 08:39:32 +0000 Subject: [PATCH 038/215] 8375747: ZGC: ZForwardingTest is unable to commit memory on Windows Reviewed-by: jsikstro, eosterlund --- src/hotspot/share/gc/z/zAddress.inline.hpp | 8 ++-- test/hotspot/gtest/gc/z/test_zForwarding.cpp | 29 ++++++++---- test/hotspot/gtest/gc/z/zunittest.hpp | 49 ++++++++++++++++++++ 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/hotspot/share/gc/z/zAddress.inline.hpp b/src/hotspot/share/gc/z/zAddress.inline.hpp index c8c8ec7ae3a..0b99802729b 100644 --- a/src/hotspot/share/gc/z/zAddress.inline.hpp +++ b/src/hotspot/share/gc/z/zAddress.inline.hpp @@ -199,18 +199,18 @@ CREATE_ZOFFSET_OPERATORS(zoffset) inline uintptr_t untype(zbacking_offset offset) { const uintptr_t value = static_cast(offset); - assert(value < ZBackingOffsetMax, "Offset out of bounds (" PTR_FORMAT " < " PTR_FORMAT ")", value, ZAddressOffsetMax); + assert(value < ZBackingOffsetMax, "Offset out of bounds (" PTR_FORMAT " < " PTR_FORMAT ")", value, ZBackingOffsetMax); return value; } inline uintptr_t untype(zbacking_offset_end offset) { const uintptr_t value = static_cast(offset); - assert(value <= ZBackingOffsetMax, "Offset out of bounds (" PTR_FORMAT " <= " PTR_FORMAT ")", value, ZAddressOffsetMax); + assert(value <= ZBackingOffsetMax, "Offset out of bounds (" PTR_FORMAT " <= " PTR_FORMAT ")", value, ZBackingOffsetMax); return value; } inline zbacking_offset to_zbacking_offset(uintptr_t value) { - assert(value < ZBackingOffsetMax, "Value out of bounds (" PTR_FORMAT " < " PTR_FORMAT ")", value, ZAddressOffsetMax); + assert(value < ZBackingOffsetMax, "Value out of bounds (" PTR_FORMAT " < " PTR_FORMAT ")", value, ZBackingOffsetMax); return zbacking_offset(value); } @@ -227,7 +227,7 @@ inline zbacking_offset_end to_zbacking_offset_end(zbacking_offset start, size_t } inline zbacking_offset_end to_zbacking_offset_end(uintptr_t value) { - assert(value <= ZBackingOffsetMax, "Value out of bounds (" PTR_FORMAT " <= " PTR_FORMAT ")", value, ZAddressOffsetMax); + assert(value <= ZBackingOffsetMax, "Value out of bounds (" PTR_FORMAT " <= " PTR_FORMAT ")", value, ZBackingOffsetMax); return zbacking_offset_end(value); } diff --git a/test/hotspot/gtest/gc/z/test_zForwarding.cpp b/test/hotspot/gtest/gc/z/test_zForwarding.cpp index 5275b78fb82..6b66d43ae7f 100644 --- a/test/hotspot/gtest/gc/z/test_zForwarding.cpp +++ b/test/hotspot/gtest/gc/z/test_zForwarding.cpp @@ -44,11 +44,12 @@ using namespace testing; class ZForwardingTest : public ZTest { public: // Setup and tear down - ZHeap* _old_heap; - ZGenerationOld* _old_old; - ZGenerationYoung* _old_young; - ZAddressReserver _zaddress_reserver; - zoffset _page_offset; + ZHeap* _old_heap; + ZGenerationOld* _old_old; + ZGenerationYoung* _old_young; + ZAddressReserver _zaddress_reserver; + ZPhysicalMemoryBackingMocker _physical_backing; + zoffset _page_offset; virtual void SetUp() { _old_heap = ZHeap::_heap; @@ -73,8 +74,16 @@ public: GTEST_SKIP() << "Unable to reserve memory"; } - char* const addr = (char*)untype(ZOffset::address_unsafe(_page_offset)); - os::commit_memory(addr, ZGranuleSize, /* executable */ false); + // Setup backing storage + _physical_backing.SetUp(ZGranuleSize); + + size_t committed = _physical_backing()->commit(zbacking_offset(0), ZGranuleSize, 0); + + if (committed != ZGranuleSize) { + GTEST_SKIP() << "Unable to commit memory"; + } + + _physical_backing()->map(ZOffset::address_unsafe(_page_offset), ZGranuleSize, zbacking_offset(0)); } virtual void TearDown() { @@ -84,10 +93,12 @@ public: ZGeneration::_young = _old_young; if (_page_offset != zoffset::invalid) { - char* const addr = (char*)untype(ZOffset::address_unsafe(_page_offset)); - os::uncommit_memory(addr, ZGranuleSize, false /* executable */); + _physical_backing()->unmap(ZOffset::address_unsafe(_page_offset), ZGranuleSize); + _physical_backing()->uncommit(zbacking_offset(0), ZGranuleSize); } + _physical_backing.TearDown(); + _zaddress_reserver.TearDown(); } diff --git a/test/hotspot/gtest/gc/z/zunittest.hpp b/test/hotspot/gtest/gc/z/zunittest.hpp index 2464f821380..13a8eb10f8a 100644 --- a/test/hotspot/gtest/gc/z/zunittest.hpp +++ b/test/hotspot/gtest/gc/z/zunittest.hpp @@ -28,6 +28,7 @@ #include "gc/z/zArguments.hpp" #include "gc/z/zInitialize.hpp" #include "gc/z/zNUMA.hpp" +#include "gc/z/zPhysicalMemoryManager.hpp" #include "gc/z/zRangeRegistry.hpp" #include "gc/z/zVirtualMemory.inline.hpp" #include "gc/z/zVirtualMemoryManager.hpp" @@ -104,6 +105,54 @@ public: } }; + class ZPhysicalMemoryBackingMocker { + size_t _old_max; + ZPhysicalMemoryBacking* _backing; + bool _active; + + static size_t set_max(size_t max_capacity) { + size_t old_max = ZBackingOffsetMax; + + ZBackingOffsetMax = max_capacity; + ZBackingIndexMax = checked_cast(ZBackingOffsetMax >> ZGranuleSizeShift); + + return old_max; + } + + public: + ZPhysicalMemoryBackingMocker() + : _old_max(0), + _backing(nullptr), + _active(false) {} + + void SetUp(size_t max_capacity) { + GTEST_EXPECT_FALSE(_active) << "SetUp called twice without a TearDown"; + + _old_max = set_max(max_capacity); + + char* const mem = (char*)os::malloc(sizeof(ZPhysicalMemoryBacking), mtTest); + _backing = new (mem) ZPhysicalMemoryBacking(ZGranuleSize); + + _active = true; + } + + void TearDown() { + GTEST_EXPECT_TRUE(_active) << "TearDown called without a preceding SetUp"; + + _active = false; + + _backing->~ZPhysicalMemoryBacking(); + os::free(_backing); + _backing = nullptr; + + set_max(_old_max); + } + + ZPhysicalMemoryBacking* operator()() { + return _backing; + } + }; + private: ZAddressOffsetMaxSetter _zaddress_offset_max_setter; unsigned int _rand_seed; From f9cc104249433eec179c98cb3fb44546254bf588 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 29 Jan 2026 08:54:37 +0000 Subject: [PATCH 039/215] 8376335: Convert PreservedMarks classes to use Atomic Reviewed-by: stefank, iwalulya --- .../share/gc/shared/preservedMarks.cpp | 30 +++++++++---------- .../share/gc/shared/preservedMarks.hpp | 7 ++--- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/gc/shared/preservedMarks.cpp b/src/hotspot/share/gc/shared/preservedMarks.cpp index 1c9f1c82e6f..605b7afe072 100644 --- a/src/hotspot/share/gc/shared/preservedMarks.cpp +++ b/src/hotspot/share/gc/shared/preservedMarks.cpp @@ -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 @@ -29,7 +29,7 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "utilities/macros.hpp" void PreservedMarks::restore() { @@ -55,15 +55,6 @@ void PreservedMarks::adjust_during_full_gc() { } } -void PreservedMarks::restore_and_increment(volatile size_t* const total_size_addr) { - const size_t stack_size = size(); - restore(); - // Only do the atomic add if the size is > 0. - if (stack_size > 0) { - AtomicAccess::add(total_size_addr, stack_size); - } -} - #ifndef PRODUCT void PreservedMarks::assert_empty() { assert(_stack.is_empty(), "stack expected to be empty, size = %zu", @@ -93,7 +84,7 @@ void PreservedMarksSet::init(uint num) { class RestorePreservedMarksTask : public WorkerTask { PreservedMarksSet* const _preserved_marks_set; SequentialSubTasksDone _sub_tasks; - volatile size_t _total_size; + Atomic _total_size; #ifdef ASSERT size_t _total_size_before; #endif // ASSERT @@ -102,7 +93,12 @@ public: void work(uint worker_id) override { uint task_id = 0; while (_sub_tasks.try_claim_task(task_id)) { - _preserved_marks_set->get(task_id)->restore_and_increment(&_total_size); + PreservedMarks* next = _preserved_marks_set->get(task_id); + size_t num_restored = next->size(); + next->restore(); + if (num_restored > 0) { + _total_size.add_then_fetch(num_restored); + } } } @@ -121,9 +117,11 @@ public: } ~RestorePreservedMarksTask() { - assert(_total_size == _total_size_before, "total_size = %zu before = %zu", _total_size, _total_size_before); - size_t mem_size = _total_size * (sizeof(oop) + sizeof(markWord)); - log_trace(gc)("Restored %zu marks, occupying %zu %s", _total_size, + size_t local_total_size = _total_size.load_relaxed(); + + assert(local_total_size == _total_size_before, "total_size = %zu before = %zu", local_total_size, _total_size_before); + size_t mem_size = local_total_size * (sizeof(oop) + sizeof(markWord)); + log_trace(gc)("Restored %zu marks, occupying %zu %s", local_total_size, byte_size_in_proper_unit(mem_size), proper_unit_for_byte_size(mem_size)); } diff --git a/src/hotspot/share/gc/shared/preservedMarks.hpp b/src/hotspot/share/gc/shared/preservedMarks.hpp index 10f75116524..3bbbd335011 100644 --- a/src/hotspot/share/gc/shared/preservedMarks.hpp +++ b/src/hotspot/share/gc/shared/preservedMarks.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, 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 @@ -59,8 +59,7 @@ public: size_t size() const { return _stack.size(); } inline void push_if_necessary(oop obj, markWord m); inline void push_always(oop obj, markWord m); - // Iterate over the stack, restore all preserved marks, and - // reclaim the memory taken up by the stack segments. + // Restore all preserved marks, and reclaim the memory taken up by the stack segments. void restore(); // Adjust the preserved mark according to its @@ -71,8 +70,6 @@ public: // to their forwarding location stored in the mark. void adjust_during_full_gc(); - void restore_and_increment(volatile size_t* const _total_size_addr); - // Assert the stack is empty and has no cached segments. void assert_empty() PRODUCT_RETURN; From 681e4ec8d37f4e30462b43e1c789d53525211b0a Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 29 Jan 2026 08:54:59 +0000 Subject: [PATCH 040/215] 8376350: Convert ReferenceProcessorPhaseTimes to use Atomic Reviewed-by: stefank, iwalulya --- .../share/gc/shared/referenceProcessorPhaseTimes.cpp | 9 ++++----- .../share/gc/shared/referenceProcessorPhaseTimes.hpp | 5 +++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp index df7d8f7b38d..0371ed2c73b 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.cpp @@ -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 @@ -30,7 +30,6 @@ #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" -#include "runtime/atomicAccess.hpp" #define ASSERT_REF_TYPE(ref_type) assert((ref_type) >= REF_SOFT && (ref_type) <= REF_PHANTOM, \ "Invariant (%d)", (int)ref_type) @@ -196,7 +195,7 @@ void ReferenceProcessorPhaseTimes::reset() { _soft_weak_final_refs_phase_worker_time_sec->reset(); for (int i = 0; i < number_of_subclasses_of_ref; i++) { - _ref_dropped[i] = 0; + _ref_dropped[i].store_relaxed(0); _ref_discovered[i] = 0; } @@ -214,7 +213,7 @@ ReferenceProcessorPhaseTimes::~ReferenceProcessorPhaseTimes() { void ReferenceProcessorPhaseTimes::add_ref_dropped(ReferenceType ref_type, size_t count) { ASSERT_REF_TYPE(ref_type); - AtomicAccess::add(&_ref_dropped[ref_type_2_index(ref_type)], count, memory_order_relaxed); + _ref_dropped[ref_type_2_index(ref_type)].add_then_fetch(count, memory_order_relaxed); } void ReferenceProcessorPhaseTimes::set_ref_discovered(ReferenceType ref_type, size_t count) { @@ -271,7 +270,7 @@ void ReferenceProcessorPhaseTimes::print_reference(ReferenceType ref_type, uint int const ref_type_index = ref_type_2_index(ref_type); size_t discovered = _ref_discovered[ref_type_index]; - size_t dropped = _ref_dropped[ref_type_index]; + size_t dropped = _ref_dropped[ref_type_index].load_relaxed(); assert(discovered >= dropped, "invariant"); size_t processed = discovered - dropped; diff --git a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp index 16691452ef4..82d26902bce 100644 --- a/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp +++ b/src/hotspot/share/gc/shared/referenceProcessorPhaseTimes.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, 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 @@ -30,6 +30,7 @@ #include "gc/shared/workerDataArray.hpp" #include "memory/allocation.hpp" #include "memory/referenceType.hpp" +#include "runtime/atomic.hpp" #include "utilities/ticks.hpp" class DiscoveredList; @@ -52,7 +53,7 @@ class ReferenceProcessorPhaseTimes : public CHeapObj { // Total spent time for reference processing. double _total_time_ms; - size_t _ref_dropped[number_of_subclasses_of_ref]; + Atomic _ref_dropped[number_of_subclasses_of_ref]; size_t _ref_discovered[number_of_subclasses_of_ref]; bool _processing_is_mt; From f96974dbbd824db8d7b2bbf28f5d3b49bb005fb3 Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Thu, 29 Jan 2026 11:30:42 +0000 Subject: [PATCH 041/215] 8373898: RepeatCompilation does not repeat compilation after bailout Reviewed-by: chagedorn, bmaillard --- src/hotspot/share/compiler/compileBroker.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index 574f4d6543b..7b236ed3589 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -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 @@ -2346,12 +2346,18 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { /* Repeat compilation without installing code for profiling purposes */ int repeat_compilation_count = directive->RepeatCompilationOption; - while (repeat_compilation_count > 0) { - ResourceMark rm(thread); - task->print_ul("NO CODE INSTALLED"); - thread->timeout()->reset(); - comp->compile_method(&ci_env, target, osr_bci, false, directive); - repeat_compilation_count--; + if (repeat_compilation_count > 0) { + CHeapStringHolder failure_reason; + failure_reason.set(ci_env._failure_reason.get()); + while (repeat_compilation_count > 0) { + ResourceMark rm(thread); + task->print_ul("NO CODE INSTALLED"); + thread->timeout()->reset(); + ci_env._failure_reason.clear(); + comp->compile_method(&ci_env, target, osr_bci, false, directive); + repeat_compilation_count--; + } + ci_env._failure_reason.set(failure_reason.get()); } } From 48846744ca96ce3c6464a1a440b9e46119dfbb88 Mon Sep 17 00:00:00 2001 From: Boris Ulasevich Date: Thu, 29 Jan 2026 12:37:51 +0000 Subject: [PATCH 042/215] 8374343: Fix SIGSEGV when lib/modules is unreadable Reviewed-by: iklam, dholmes --- src/hotspot/share/classfile/classLoader.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index d9a63cd154b..f631bfaa102 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -1418,6 +1418,10 @@ char* ClassLoader::lookup_vm_options() { jio_snprintf(modules_path, JVM_MAXPATHLEN, "%s%slib%smodules", Arguments::get_java_home(), fileSep, fileSep); JImage_file =(*JImageOpen)(modules_path, &error); if (JImage_file == nullptr) { + if (Arguments::has_jimage()) { + // The modules file exists but is unreadable or corrupt + vm_exit_during_initialization(err_msg("Unable to load %s", modules_path)); + } return nullptr; } From e85d5d7a16024f6a3eda14f1e08f72e07ae38dd0 Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Thu, 29 Jan 2026 12:43:48 +0000 Subject: [PATCH 043/215] 8375010: C2 VectorAPI: assert(vbox->is_CheckCastPP()) failed: should be expanded 8374903: C2 VectorAPI: assert(vbox->as_Phi()->region() == vect->as_Phi()->region()) failed Reviewed-by: qamai, vlivanov --- src/hotspot/share/opto/vector.cpp | 38 +++---------- .../vectorapi/VectorBoxExpandPhi.java | 50 +++++++++++++++++ .../vectorapi/VectorBoxExpandProj.java | 54 +++++++++++++++++++ 3 files changed, 112 insertions(+), 30 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandPhi.java create mode 100644 test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandProj.java diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index cf01b2442e6..f44df7e6da2 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -326,37 +326,15 @@ Node* PhaseVector::expand_vbox_node_helper(Node* vbox, return expand_vbox_alloc_node(vbox_alloc, vect, box_type, vect_type); } - // Handle the case when both the allocation input and vector input to - // VectorBoxNode are Phi. This case is generated after the transformation of - // Phi: Phi (VectorBox1 VectorBox2) => VectorBox (Phi1 Phi2). - // With this optimization, the relative two allocation inputs of VectorBox1 and - // VectorBox2 are gathered into Phi1 now. Similarly, the original vector - // inputs of two VectorBox nodes are in Phi2. - // - // See PhiNode::merge_through_phi in cfg.cpp for more details. - if (vbox->is_Phi() && vect->is_Phi()) { - assert(vbox->as_Phi()->region() == vect->as_Phi()->region(), ""); + // Handle the case when the allocation input to VectorBoxNode is a Phi. + // This is generated after the transformation in PhiNode::merge_through_phi: + // Phi (VectorBox1 VectorBox2) => VectorBox (Phi1 Phi2) + // The vector input may also be a Phi (Phi2 above), or it may have been + // value-numbered to a single node if all inputs were identical. + if (vbox->is_Phi()) { + bool same_region = vect->is_Phi() && vbox->as_Phi()->region() == vect->as_Phi()->region(); for (uint i = 1; i < vbox->req(); i++) { - Node* new_box = expand_vbox_node_helper(vbox->in(i), vect->in(i), - box_type, vect_type, visited); - if (!new_box->is_Phi()) { - C->initial_gvn()->hash_delete(vbox); - vbox->set_req(i, new_box); - } - } - return C->initial_gvn()->transform(vbox); - } - - // Handle the case when the allocation input to VectorBoxNode is a phi - // but the vector input is not, which can definitely be the case if the - // vector input has been value-numbered. It seems to be safe to do by - // construction because VectorBoxNode and VectorBoxAllocate come in a - // specific order as a result of expanding an intrinsic call. After that, if - // any of the inputs to VectorBoxNode are value-numbered they can only - // move up and are guaranteed to dominate. - if (vbox->is_Phi() && (vect->is_Vector() || vect->is_LoadVector())) { - for (uint i = 1; i < vbox->req(); i++) { - Node* new_box = expand_vbox_node_helper(vbox->in(i), vect, + Node* new_box = expand_vbox_node_helper(vbox->in(i), same_region ? vect->in(i) : vect, box_type, vect_type, visited); if (!new_box->is_Phi()) { C->initial_gvn()->hash_delete(vbox); diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandPhi.java b/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandPhi.java new file mode 100644 index 00000000000..936d24e4767 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandPhi.java @@ -0,0 +1,50 @@ +/* + * 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.vectorapi; + +import jdk.incubator.vector.*; + +/* + * @test + * @bug 8374903 + * @summary C2 crashes when VectorBox Phi and vector Phi have different regions + * @modules jdk.incubator.vector + * @library /test/lib + * + * @run main/othervm -Xbatch -XX:CompileCommand=compileonly,compiler.vectorapi.VectorBoxExpandPhi::test compiler.vectorapi.VectorBoxExpandPhi + */ +public class VectorBoxExpandPhi { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(); + } + } + + public static Object test() { + var v0 = DoubleVector.broadcast(DoubleVector.SPECIES_128, 1.0); + var v1 = (FloatVector)v0.convertShape(VectorOperators.Conversion.ofCast(double.class, float.class), FloatVector.SPECIES_64, 0); + var v2 = (FloatVector)v1.convertShape(VectorOperators.Conversion.ofReinterpret(float.class, float.class), FloatVector.SPECIES_64, 0); + return v2; + } +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandProj.java b/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandProj.java new file mode 100644 index 00000000000..5d152834b17 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorBoxExpandProj.java @@ -0,0 +1,54 @@ +/* + * 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.vectorapi; + +import jdk.incubator.vector.*; + +/* + * @test + * @bug 8375010 + * @summary C2 crashes when expanding VectorBox with Proj input from vector math call + * @modules jdk.incubator.vector + * @library /test/lib + * + * @run main/othervm -Xbatch -XX:CompileCommand=compileonly,compiler.vectorapi.VectorBoxExpandProj::test compiler.vectorapi.VectorBoxExpandProj + */ +public class VectorBoxExpandProj { + static boolean b; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + b = !b; + test(); + } + System.out.println("PASS"); + } + + // TAN returns Proj (not VectorNode). Phi merging creates VectorBox(Phi, Proj). + // expand_vbox_node_helper must handle Proj inputs with vector type. + static Object test() { + var t = DoubleVector.broadcast(DoubleVector.SPECIES_128, 1.0).lanewise(VectorOperators.TAN); + return b ? t.convertShape(VectorOperators.Conversion.ofCast(double.class, double.class), DoubleVector.SPECIES_128, 0) : t; + } +} From 99119597aa95c1139ae2259bed5ec885a7c01269 Mon Sep 17 00:00:00 2001 From: Ferenc Rakoczi Date: Thu, 29 Jan 2026 12:52:23 +0000 Subject: [PATCH 044/215] 8374755: ML-KEM's 12-bit decompression can be simplified on aarch64 Reviewed-by: adinn --- .../cpu/aarch64/stubGenerator_aarch64.cpp | 83 +++---------------- .../com/sun/crypto/provider/ML_KEM.java | 22 ++--- 2 files changed, 18 insertions(+), 87 deletions(-) diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 7e2f333ba40..db653bcf236 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -6081,14 +6081,18 @@ class StubGenerator: public StubCodeGenerator { // static int implKyber12To16( // byte[] condensed, int index, short[] parsed, int parsedLength) {} // - // (parsedLength or (parsedLength - 48) must be divisible by 64.) + // we assume that parsed and condensed are allocated such that for + // n = (parsedLength + 63) / 64 + // n blocks of 96 bytes of input can be processed, i.e. + // index + n * 96 <= condensed.length and + // n * 64 <= parsed.length // // condensed (byte[]) = c_rarg0 // condensedIndex = c_rarg1 - // parsed (short[112 or 256]) = c_rarg2 - // parsedLength (112 or 256) = c_rarg3 + // parsed (short[]) = c_rarg2 + // parsedLength = c_rarg3 address generate_kyber12To16() { - Label L_F00, L_loop, L_end; + Label L_F00, L_loop; __ align(CodeEntryAlignment); StubId stub_id = StubId::stubgen_kyber12To16_id; @@ -6209,75 +6213,8 @@ class StubGenerator: public StubCodeGenerator { vs_st2_post(vs_front(vb), __ T8H, parsed); __ sub(parsedLength, parsedLength, 64); - __ cmp(parsedLength, (u1)64); - __ br(Assembler::GE, L_loop); - __ cbz(parsedLength, L_end); - - // if anything is left it should be a final 72 bytes of input - // i.e. a final 48 12-bit values. so we handle this by loading - // 48 bytes into all 16B lanes of front(vin) and only 24 - // bytes into the lower 8B lane of back(vin) - vs_ld3_post(vs_front(vin), __ T16B, condensed); - vs_ld3(vs_back(vin), __ T8B, condensed); - - // Expand vin[0] into va[0:1], and vin[1] into va[2:3] and va[4:5] - // n.b. target elements 2 and 3 of va duplicate elements 4 and - // 5 and target element 2 of vb duplicates element 4. - __ ushll(va[0], __ T8H, vin[0], __ T8B, 0); - __ ushll2(va[1], __ T8H, vin[0], __ T16B, 0); - __ ushll(va[2], __ T8H, vin[1], __ T8B, 0); - __ ushll2(va[3], __ T8H, vin[1], __ T16B, 0); - __ ushll(va[4], __ T8H, vin[1], __ T8B, 0); - __ ushll2(va[5], __ T8H, vin[1], __ T16B, 0); - - // This time expand just the lower 8 lanes - __ ushll(vb[0], __ T8H, vin[3], __ T8B, 0); - __ ushll(vb[2], __ T8H, vin[4], __ T8B, 0); - __ ushll(vb[4], __ T8H, vin[4], __ T8B, 0); - - // shift lo byte of copy 1 of the middle stripe into the high byte - __ shl(va[2], __ T8H, va[2], 8); - __ shl(va[3], __ T8H, va[3], 8); - __ shl(vb[2], __ T8H, vb[2], 8); - - // expand vin[2] into va[6:7] and lower 8 lanes of vin[5] into - // vb[6] pre-shifted by 4 to ensure top bits of the input 12-bit - // int are in bit positions [4..11]. - __ ushll(va[6], __ T8H, vin[2], __ T8B, 4); - __ ushll2(va[7], __ T8H, vin[2], __ T16B, 4); - __ ushll(vb[6], __ T8H, vin[5], __ T8B, 4); - - // mask hi 4 bits of each 1st 12-bit int in pair from copy1 and - // shift lo 4 bits of each 2nd 12-bit int in pair to bottom of - // copy2 - __ andr(va[2], __ T16B, va[2], v31); - __ andr(va[3], __ T16B, va[3], v31); - __ ushr(va[4], __ T8H, va[4], 4); - __ ushr(va[5], __ T8H, va[5], 4); - __ andr(vb[2], __ T16B, vb[2], v31); - __ ushr(vb[4], __ T8H, vb[4], 4); - - - - // sum hi 4 bits and lo 8 bits of each 1st 12-bit int in pair and - // hi 8 bits plus lo 4 bits of each 2nd 12-bit int in pair - - // n.b. ordering ensures: i) inputs are consumed before they are - // overwritten ii) order of 16-bit results across succsessive - // pairs of vectors in va and then lower half of vb reflects order - // of corresponding 12-bit inputs - __ addv(va[0], __ T8H, va[0], va[2]); - __ addv(va[2], __ T8H, va[1], va[3]); - __ addv(va[1], __ T8H, va[4], va[6]); - __ addv(va[3], __ T8H, va[5], va[7]); - __ addv(vb[0], __ T8H, vb[0], vb[2]); - __ addv(vb[1], __ T8H, vb[4], vb[6]); - - // store 48 results interleaved as shorts - vs_st2_post(vs_front(va), __ T8H, parsed); - vs_st2_post(vs_front(vs_front(vb)), __ T8H, parsed); - - __ BIND(L_end); + __ cmp(parsedLength, (u1)0); + __ br(Assembler::GT, L_loop); __ leave(); // required for proper stackwalking of RuntimeStub frame __ mov(r0, zr); // return 0 diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java index 1280ccdad74..bf6576f4d93 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java @@ -1353,22 +1353,16 @@ public final class ML_KEM { } } - // The intrinsic implementations assume that the input and output buffers - // are such that condensed can be read in 96-byte chunks and - // parsed can be written in 64 shorts chunks except for the last chunk - // that can be either 48 or 64 shorts. In other words, - // if (i - 1) * 64 < parsedLengths <= i * 64 then - // parsed.length should be either i * 64 or (i-1) * 64 + 48 and - // condensed.length should be at least index + i * 96. + // An intrinsic implementation assumes that the input and output buffers + // are such that condensed can be read in chunks of 192 bytes and + // parsed can be written in chunks of 128 shorts, so callers should allocate + // the condensed and parsed arrays accordingly, see the assert() private void twelve2Sixteen(byte[] condensed, int index, short[] parsed, int parsedLength) { - int i = parsedLength / 64; - int remainder = parsedLength - i * 64; - if (remainder != 0) { - i++; - } - assert ((remainder == 0) || (remainder == 48)) && - (index + i * 96 <= condensed.length); + int n = (parsedLength + 127) / 128; + assert ((parsed.length >= n * 128) && + (condensed.length >= index + n * 192)); + implKyber12To16(condensed, index, parsed, parsedLength); } From 7c6c34e150cf01cec5d166f6cbb8a649c75b0627 Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Thu, 29 Jan 2026 13:11:47 +0000 Subject: [PATCH 045/215] 8370502: C2: segfault while adding node to IGVN worklist Reviewed-by: mhaessig, dlong --- src/hotspot/share/opto/macro.cpp | 22 ++++---- .../c2/TestUnlockNodeNullMemprof.java | 53 +++++++++++++++++++ 2 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 56262d226fc..9470001b2d2 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2322,12 +2322,7 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) { // No need for a null check on unlock // Make the merge point - Node *region; - Node *mem_phi; - - region = new RegionNode(3); - // create a Phi for the memory state - mem_phi = new PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); + Node* region = new RegionNode(3); FastUnlockNode *funlock = new FastUnlockNode( ctrl, obj, box ); funlock = transform_later( funlock )->as_FastUnlock(); @@ -2356,12 +2351,15 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) { transform_later(region); _igvn.replace_node(_callprojs.fallthrough_proj, region); - Node *memproj = transform_later(new ProjNode(call, TypeFunc::Memory) ); - mem_phi->init_req(1, memproj ); - mem_phi->init_req(2, mem); - transform_later(mem_phi); - - _igvn.replace_node(_callprojs.fallthrough_memproj, mem_phi); + if (_callprojs.fallthrough_memproj != nullptr) { + // create a Phi for the memory state + Node* mem_phi = new PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); + Node* memproj = transform_later(new ProjNode(call, TypeFunc::Memory)); + mem_phi->init_req(1, memproj); + mem_phi->init_req(2, mem); + transform_later(mem_phi); + _igvn.replace_node(_callprojs.fallthrough_memproj, mem_phi); + } } void PhaseMacroExpand::expand_subtypecheck_node(SubTypeCheckNode *check) { diff --git a/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java b/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java new file mode 100644 index 00000000000..f86dc495fd2 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestUnlockNodeNullMemprof.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 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. + */ + +/** + * @test + * @bug 8370502 + * @summary Do not segfault while adding node to IGVN worklist + * + * @run main/othervm -Xbatch ${test.main.class} + */ + +package compiler.c2; + +public class TestUnlockNodeNullMemprof { + public static void main(String[] args) { + int[] a = new int[0]; // test only valid when size is 0. + for (int i = 0; i < Integer.valueOf(10000); i++) // test only valid with boxed loop limit + try { + test(a); + } catch (ArrayIndexOutOfBoundsException e) { + } + } + + static void test(int[] a) { + for (int i = 0; i < 1;) { + a[i] = 0; + synchronized (TestUnlockNodeNullMemprof.class) { + } + for (int j = 0; Integer.valueOf(j) < 1;) + j = 0; + } + } +} From a54ff1bff45e1cb30100cbaa253494c3462f7abd Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Thu, 29 Jan 2026 16:29:34 +0000 Subject: [PATCH 046/215] 8376523: Move interned strings into AOT heap roots array Reviewed-by: kvn, shade --- src/hotspot/share/cds/aotMappedHeapLoader.cpp | 4 +- src/hotspot/share/cds/aotMetaspace.cpp | 6 - src/hotspot/share/cds/heapShared.cpp | 12 +- src/hotspot/share/cds/heapShared.hpp | 3 +- src/hotspot/share/classfile/stringTable.cpp | 184 ++---------------- src/hotspot/share/classfile/stringTable.hpp | 43 +--- .../sharedStrings/SharedStringsStress.java | 4 +- 7 files changed, 27 insertions(+), 229 deletions(-) diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.cpp b/src/hotspot/share/cds/aotMappedHeapLoader.cpp index a8678ed757f..146228436f4 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.cpp @@ -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 @@ -465,8 +465,6 @@ void AOTMappedHeapLoader::finish_initialization(FileMapInfo* info) { assert(segment_oop->is_objArray(), "Must be"); add_root_segment((objArrayOop)segment_oop); } - - StringTable::load_shared_strings_array(); } } diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 683c897d855..62d76957c0a 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1162,12 +1162,6 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS // Perhaps there is a way to avoid hard-coding these names here. // See discussion in JDK-8342481. } - - if (HeapShared::is_writing_mapping_mode()) { - // Do this at the very end, when no Java code will be executed. Otherwise - // some new strings may be added to the intern table. - StringTable::allocate_shared_strings_array(CHECK); - } } else { log_info(aot)("Not dumping heap, reset CDSConfig::_is_using_optimized_module_handling"); CDSConfig::stop_using_optimized_module_handling(); diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 89694c6780e..42129011612 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -413,6 +413,8 @@ void HeapShared::materialize_thread_object() { void HeapShared::add_to_dumped_interned_strings(oop string) { assert(HeapShared::is_writing_mapping_mode(), "Only used by this mode"); AOTMappedHeapWriter::add_to_dumped_interned_strings(string); + bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, string); + assert(success, "shared strings array must not point to arrays or strings that are too large to archive"); } void HeapShared::finalize_initialization(FileMapInfo* static_mapinfo) { @@ -831,14 +833,6 @@ static objArrayOop get_archived_resolved_references(InstanceKlass* src_ik) { return nullptr; } -void HeapShared::archive_strings() { - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - oop shared_strings_array = StringTable::init_shared_strings_array(); - bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, shared_strings_array); - assert(success, "shared strings array must not point to arrays or strings that are too large to archive"); - StringTable::set_shared_strings_array_index(append_root(shared_strings_array)); -} - int HeapShared::archive_exception_instance(oop exception) { bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, exception); assert(success, "sanity"); @@ -890,7 +884,7 @@ void HeapShared::start_scanning_for_oops() { void HeapShared::end_scanning_for_oops() { if (is_writing_mapping_mode()) { - archive_strings(); + StringTable::init_shared_table(); } delete_seen_objects_table(); } diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 118c60faa60..3c7068e96ab 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.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 @@ -478,7 +478,6 @@ private: static bool has_been_archived(oop orig_obj); static void prepare_resolved_references(); - static void archive_strings(); static void archive_subgraphs(); static void copy_java_mirror(oop orig_mirror, oop scratch_m); diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index 20dfad0d980..bbc12c8dcab 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -74,24 +74,9 @@ const size_t REHASH_LEN = 100; const double CLEAN_DEAD_HIGH_WATER_MARK = 0.5; #if INCLUDE_CDS_JAVA_HEAP -bool StringTable::_is_two_dimensional_shared_strings_array = false; -OopHandle StringTable::_shared_strings_array; -int StringTable::_shared_strings_array_root_index; - inline oop StringTable::read_string_from_compact_hashtable(address base_address, u4 index) { assert(AOTMappedHeapLoader::is_in_use(), "sanity"); - objArrayOop array = (objArrayOop)(_shared_strings_array.resolve()); - oop s; - - if (!_is_two_dimensional_shared_strings_array) { - s = array->obj_at((int)index); - } else { - int primary_index = index >> _secondary_array_index_bits; - int secondary_index = index & _secondary_array_index_mask; - objArrayOop secondary = (objArrayOop)array->obj_at(primary_index); - s = secondary->obj_at(secondary_index); - } - + oop s = HeapShared::get_root((int)index, false); assert(java_lang_String::is_instance(s), "must be"); return s; } @@ -115,7 +100,6 @@ OopStorage* StringTable::_oop_storage; static size_t _current_size = 0; static volatile size_t _items_count = 0; -DEBUG_ONLY(static bool _disable_interning_during_cds_dump = false); volatile bool _alt_hash = false; @@ -317,12 +301,6 @@ void StringTable::create_table() { _oop_storage->register_num_dead_callback(&gc_notification); } -#if INCLUDE_CDS_JAVA_HEAP -void StringTable::load_shared_strings_array() { - _shared_strings_array = OopHandle(Universe::vm_global(), HeapShared::get_root(_shared_strings_array_root_index)); -} -#endif - void StringTable::item_added() { AtomicAccess::inc(&_items_count); } @@ -509,9 +487,6 @@ oop StringTable::intern(const char* utf8_string, TRAPS) { } oop StringTable::intern(const StringWrapper& name, TRAPS) { - assert(!AtomicAccess::load_acquire(&_disable_interning_during_cds_dump), - "All threads that may intern strings should have been stopped before CDS starts copying the interned string table"); - // shared table always uses java_lang_String::hash_code unsigned int hash = hash_wrapped_string(name); oop found_string = lookup_shared(name, hash); @@ -957,118 +932,13 @@ oop StringTable::lookup_shared(const jchar* name, int len) { return _shared_table.lookup(wrapped_name, java_lang_String::hash_code(name, len), 0); } -// This is called BEFORE we enter the CDS safepoint. We can still allocate Java object arrays to -// be used by the shared strings table. -void StringTable::allocate_shared_strings_array(TRAPS) { - if (!CDSConfig::is_dumping_heap()) { - return; - } +void StringTable::init_shared_table() { + assert(SafepointSynchronize::is_at_safepoint(), "inside AOT safepoint"); + precond(CDSConfig::is_dumping_heap()); + assert(HeapShared::is_writing_mapping_mode(), "not used for streamed oops"); - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - - CompileBroker::wait_for_no_active_tasks(); - - precond(CDSConfig::allow_only_single_java_thread()); - - // At this point, no more strings will be added: - // - There's only a single Java thread (this thread). It no longer executes Java bytecodes - // so JIT compilation will eventually stop. - // - CompileBroker has no more active tasks, so all JIT requests have been processed. - - // This flag will be cleared after intern table dumping has completed, so we can run the - // compiler again (for future AOT method compilation, etc). - DEBUG_ONLY(AtomicAccess::release_store(&_disable_interning_during_cds_dump, true)); - - if (items_count_acquire() > (size_t)max_jint) { - fatal("Too many strings to be archived: %zu", items_count_acquire()); - } - - int total = (int)items_count_acquire(); - size_t single_array_size = objArrayOopDesc::object_size(total); - - log_info(aot)("allocated string table for %d strings", total); - - if (!HeapShared::is_too_large_to_archive(single_array_size)) { - // The entire table can fit in a single array - objArrayOop array = oopFactory::new_objArray(vmClasses::Object_klass(), total, CHECK); - _shared_strings_array = OopHandle(Universe::vm_global(), array); - log_info(aot)("string table array (single level) length = %d", total); - } else { - // Split the table in two levels of arrays. - int primary_array_length = (total + _secondary_array_max_length - 1) / _secondary_array_max_length; - size_t primary_array_size = objArrayOopDesc::object_size(primary_array_length); - size_t secondary_array_size = objArrayOopDesc::object_size(_secondary_array_max_length); - - if (HeapShared::is_too_large_to_archive(secondary_array_size)) { - // This can only happen if you have an extremely large number of classes that - // refer to more than 16384 * 16384 = 26M interned strings! Not a practical concern - // but bail out for safety. - log_error(aot)("Too many strings to be archived: %zu", items_count_acquire()); - AOTMetaspace::unrecoverable_writing_error(); - } - - objArrayOop primary = oopFactory::new_objArray(vmClasses::Object_klass(), primary_array_length, CHECK); - objArrayHandle primaryHandle(THREAD, primary); - _shared_strings_array = OopHandle(Universe::vm_global(), primary); - - log_info(aot)("string table array (primary) length = %d", primary_array_length); - for (int i = 0; i < primary_array_length; i++) { - int len; - if (total > _secondary_array_max_length) { - len = _secondary_array_max_length; - } else { - len = total; - } - total -= len; - - objArrayOop secondary = oopFactory::new_objArray(vmClasses::Object_klass(), len, CHECK); - primaryHandle()->obj_at_put(i, secondary); - - log_info(aot)("string table array (secondary)[%d] length = %d", i, len); - assert(!HeapShared::is_too_large_to_archive(secondary), "sanity"); - } - - assert(total == 0, "must be"); - _is_two_dimensional_shared_strings_array = true; - } -} - -#ifndef PRODUCT -void StringTable::verify_secondary_array_index_bits() { - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - int max; - for (max = 1; ; max++) { - size_t next_size = objArrayOopDesc::object_size(1 << (max + 1)); - if (HeapShared::is_too_large_to_archive(next_size)) { - break; - } - } - // Currently max is 17 for +UseCompressedOops, 16 for -UseCompressedOops. - // When we add support for Shenandoah (which has a smaller mininum region size than G1), - // max will become 15/14. - // - // We use _secondary_array_index_bits==14 as that will be the eventual value, and will - // make testing easier. - assert(_secondary_array_index_bits <= max, - "_secondary_array_index_bits (%d) must be smaller than max possible value (%d)", - _secondary_array_index_bits, max); -} -#endif // PRODUCT - -// This is called AFTER we enter the CDS safepoint. -// -// For each shared string: -// [1] Store it into _shared_strings_array. Encode its position as a 32-bit index. -// [2] Store the index and hashcode into _shared_table. -oop StringTable::init_shared_strings_array() { - assert(CDSConfig::is_dumping_heap(), "must be"); - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - objArrayOop array = (objArrayOop)(_shared_strings_array.resolve()); - - verify_secondary_array_index_bits(); - - int index = 0; - auto copy_into_array = [&] (WeakHandle* val) { + int n = 0; + auto copy_into_aot_heap = [&] (WeakHandle* val) { oop string = val->peek(); if (string != nullptr && !HeapShared::is_string_too_large_to_archive(string)) { // If string is too large, don't put it into the string table. @@ -1077,53 +947,34 @@ oop StringTable::init_shared_strings_array() { // - If there's a reference to it, we will report an error inside HeapShared.cpp and // dumping will fail. HeapShared::add_to_dumped_interned_strings(string); - if (!_is_two_dimensional_shared_strings_array) { - assert(index < array->length(), "no strings should have been added"); - array->obj_at_put(index, string); - } else { - int primary_index = index >> _secondary_array_index_bits; - int secondary_index = index & _secondary_array_index_mask; - - assert(primary_index < array->length(), "no strings should have been added"); - objArrayOop secondary = (objArrayOop)array->obj_at(primary_index); - - assert(secondary != nullptr && secondary->is_objArray(), "must be"); - assert(secondary_index < secondary->length(), "no strings should have been added"); - secondary->obj_at_put(secondary_index, string); - } - index ++; } + n++; return true; }; - _local_table->do_safepoint_scan(copy_into_array); - log_info(aot)("Archived %d interned strings", index); - return array; + _local_table->do_safepoint_scan(copy_into_aot_heap); + log_info(aot)("Archived %d interned strings", n); }; void StringTable::write_shared_table() { + assert(SafepointSynchronize::is_at_safepoint(), "inside AOT safepoint"); + precond(CDSConfig::is_dumping_heap()); + assert(HeapShared::is_writing_mapping_mode(), "not used for streamed oops"); + _shared_table.reset(); CompactHashtableWriter writer((int)items_count_acquire(), ArchiveBuilder::string_stats()); - int index = 0; auto copy_into_shared_table = [&] (WeakHandle* val) { oop string = val->peek(); if (string != nullptr && !HeapShared::is_string_too_large_to_archive(string)) { unsigned int hash = java_lang_String::hash_code(string); - writer.add(hash, index); - index ++; + int root_id = HeapShared::append_root(string); + writer.add(hash, root_id); } return true; }; _local_table->do_safepoint_scan(copy_into_shared_table); writer.dump(&_shared_table, "string"); - - DEBUG_ONLY(AtomicAccess::release_store(&_disable_interning_during_cds_dump, false)); -} - -void StringTable::set_shared_strings_array_index(int root_index) { - assert(HeapShared::is_writing_mapping_mode(), "should not reach here"); - _shared_strings_array_root_index = root_index; } void StringTable::serialize_shared_table_header(SerializeClosure* soc) { @@ -1135,8 +986,5 @@ void StringTable::serialize_shared_table_header(SerializeClosure* soc) { } else if (!AOTMappedHeapLoader::is_in_use()) { _shared_table.reset(); } - - soc->do_bool(&_is_two_dimensional_shared_strings_array); - soc->do_int(&_shared_strings_array_root_index); } #endif //INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/stringTable.hpp b/src/hotspot/share/classfile/stringTable.hpp index 839e9d9053d..94a0db5b5a5 100644 --- a/src/hotspot/share/classfile/stringTable.hpp +++ b/src/hotspot/share/classfile/stringTable.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 @@ -109,48 +109,15 @@ public: static bool needs_rehashing() { return _needs_rehashing; } static inline void update_needs_rehash(bool rehash); - // Sharing -#if INCLUDE_CDS_JAVA_HEAP - static inline oop read_string_from_compact_hashtable(address base_address, u4 index); - + // AOT support + static inline oop read_string_from_compact_hashtable(address base_address, u4 index) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); private: - static bool _is_two_dimensional_shared_strings_array; - static OopHandle _shared_strings_array; - static int _shared_strings_array_root_index; - - // All the shared strings are referenced through _shared_strings_array to keep them alive. - // Each shared string is stored as a 32-bit index in ::_shared_table. The index - // is interpreted in two ways: - // - // [1] _is_two_dimensional_shared_strings_array = false: _shared_strings_array is an Object[]. - // Each shared string is stored as _shared_strings_array[index] - // - // [2] _is_two_dimensional_shared_strings_array = true: _shared_strings_array is an Object[][] - // This happens when there are too many elements in the shared table. We store them - // using two levels of objArrays, such that none of the arrays are too big for - // AOTMappedHeapWriter::is_too_large_to_archive(). In this case, the index is splited into two - // parts. Each shared string is stored as _shared_strings_array[primary_index][secondary_index]: - // - // [bits 31 .. 14][ bits 13 .. 0 ] - // primary_index secondary_index - const static int _secondary_array_index_bits = 14; - const static int _secondary_array_max_length = 1 << _secondary_array_index_bits; - const static int _secondary_array_index_mask = _secondary_array_max_length - 1; - - // make sure _secondary_array_index_bits is not too big - static void verify_secondary_array_index_bits() PRODUCT_RETURN; -#endif // INCLUDE_CDS_JAVA_HEAP - - private: static oop lookup_shared(const StringWrapper& name, unsigned int hash) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); - public: +public: static oop lookup_shared(const jchar* name, int len) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); static size_t shared_entry_count() NOT_CDS_JAVA_HEAP_RETURN_(0); - static void allocate_shared_strings_array(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; - static void load_shared_strings_array() NOT_CDS_JAVA_HEAP_RETURN; - static oop init_shared_strings_array() NOT_CDS_JAVA_HEAP_RETURN_(nullptr); + static void init_shared_table() NOT_CDS_JAVA_HEAP_RETURN; static void write_shared_table() NOT_CDS_JAVA_HEAP_RETURN; - static void set_shared_strings_array_index(int root_index) NOT_CDS_JAVA_HEAP_RETURN; static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; // Jcmd diff --git a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java index 4d176c949c6..dc677756ffa 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/sharedStrings/SharedStringsStress.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 @@ -80,8 +80,6 @@ public class SharedStringsStress { "-Xlog:gc+region+cds", "-Xlog:gc+region=trace")); TestCommon.checkDump(dumpOutput); - dumpOutput.shouldContain("string table array (primary)"); - dumpOutput.shouldContain("string table array (secondary)"); // We could create up to 26MB of archived heap objects. Run with enough Xms to ensure // SerialGC can accommodate the archived objects during VM start up. From 847b5166ea6322f9ff3effa62ed6d1e73a8b1122 Mon Sep 17 00:00:00 2001 From: Matthew Donovan Date: Thu, 29 Jan 2026 16:44:24 +0000 Subject: [PATCH 047/215] 8373018: Update OpenSSL version to 3.5.4 Reviewed-by: abarashev, weijun --- test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java b/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java index 6fa8cb2e408..f66ad542e47 100644 --- a/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.java +++ b/test/lib/jdk/test/lib/security/OpensslArtifactFetcher.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 @@ -33,7 +33,7 @@ import jtreg.SkippedException; public class OpensslArtifactFetcher { - private static final String OPENSSL_BUNDLE_VERSION = "3.5.1"; + private static final String OPENSSL_BUNDLE_VERSION = "3.5.4"; private static final String OPENSSL_ORG = "jpg.tests.jdk.openssl"; /** From 69c868d5b7fdeaf38d6a45b75d68bf51b6ee7188 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 29 Jan 2026 18:54:39 +0000 Subject: [PATCH 048/215] 8376510: Raster.createBandedRaster(int, int, int, int, int[], int[], Point) does not check for negative scanlineStride Reviewed-by: serb, azvegint --- src/java.desktop/share/classes/java/awt/image/Raster.java | 5 ++++- .../java/awt/image/Raster/CreateRasterExceptionTest.java | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/image/Raster.java b/src/java.desktop/share/classes/java/awt/image/Raster.java index 053e7c1eec5..3312a39b693 100644 --- a/src/java.desktop/share/classes/java/awt/image/Raster.java +++ b/src/java.desktop/share/classes/java/awt/image/Raster.java @@ -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 @@ -452,6 +452,9 @@ public class Raster { throw new IllegalArgumentException("Dimensions (width="+w+ " height="+h+") are too large"); } + if (scanlineStride < 0) { + throw new IllegalArgumentException("Scanline stride must be >= 0"); + } if (bankIndices == null) { throw new ArrayIndexOutOfBoundsException("Bank indices array is null"); diff --git a/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java b/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java index d89de44401b..13a88e8c590 100644 --- a/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.java +++ b/test/jdk/java/awt/image/Raster/CreateRasterExceptionTest.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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8255800 8369129 + * @bug 8255800 8369129 8376297 * @summary verify Raster + SampleModel creation vs spec. */ @@ -739,7 +739,7 @@ public class CreateRasterExceptionTest { /* @throws IllegalArgumentException if * {@code scanlineStride} is less than 0 */ - Raster.createBandedRaster(DataBuffer.TYPE_INT, 1, 1, -3, + Raster.createBandedRaster(DataBuffer.TYPE_INT, 10, 10, -1000, bankIndices, bandOffsets, null); noException(); } catch (IllegalArgumentException t) { From 9470aa31175b504fcef15a932825dbc9e0532234 Mon Sep 17 00:00:00 2001 From: Anupam Dev Date: Thu, 29 Jan 2026 18:59:11 +0000 Subject: [PATCH 049/215] 8375011: OldJTable.java - NullPointerException when columnData is null Reviewed-by: prr, psadhukhan, tr --- .../share/jfc/TableExample/OldJTable.java | 264 ------------------ 1 file changed, 264 deletions(-) delete mode 100644 src/demo/share/jfc/TableExample/OldJTable.java diff --git a/src/demo/share/jfc/TableExample/OldJTable.java b/src/demo/share/jfc/TableExample/OldJTable.java deleted file mode 100644 index 8c77978fe8a..00000000000 --- a/src/demo/share/jfc/TableExample/OldJTable.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of Oracle nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This source code is provided to illustrate the usage of a given feature - * or technique and has been deliberately simplified. Additional steps - * required for a production-quality application, such as security checks, - * input validation and proper error handling, might not be present in - * this sample code. - */ - - - -import java.util.EventObject; -import java.util.List; -import javax.swing.JTable; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableCellEditor; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; - - -/** - * The OldJTable is an unsupported class containing some methods that were - * deleted from the JTable between releases 0.6 and 0.7 - */ -@SuppressWarnings("serial") -public class OldJTable extends JTable -{ - /* - * A new convenience method returning the index of the column in the - * co-ordinate space of the view. - */ - public int getColumnIndex(Object identifier) { - return getColumnModel().getColumnIndex(identifier); - } - -// -// Methods deleted from the JTable because they only work with the -// DefaultTableModel. -// - - public TableColumn addColumn(Object columnIdentifier, int width) { - return addColumn(columnIdentifier, width, null, null, null); - } - - public TableColumn addColumn(Object columnIdentifier, List columnData) { - return addColumn(columnIdentifier, -1, null, null, columnData); - } - - // Override the new JTable implementation - it will not add a column to the - // DefaultTableModel. - public TableColumn addColumn(Object columnIdentifier, int width, - TableCellRenderer renderer, - TableCellEditor editor) { - return addColumn(columnIdentifier, width, renderer, editor, null); - } - - public TableColumn addColumn(Object columnIdentifier, int width, - TableCellRenderer renderer, - TableCellEditor editor, List columnData) { - checkDefaultTableModel(); - - // Set up the model side first - DefaultTableModel m = (DefaultTableModel)getModel(); - m.addColumn(columnIdentifier, columnData.toArray()); - - // The column will have been added to the end, so the index of the - // column in the model is the last element. - TableColumn newColumn = new TableColumn( - m.getColumnCount()-1, width, renderer, editor); - super.addColumn(newColumn); - return newColumn; - } - - // Not possilble to make this work the same way ... change it so that - // it does not delete columns from the model. - public void removeColumn(Object columnIdentifier) { - super.removeColumn(getColumn(columnIdentifier)); - } - - public void addRow(Object[] rowData) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).addRow(rowData); - } - - public void addRow(List rowData) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).addRow(rowData.toArray()); - } - - public void removeRow(int rowIndex) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).removeRow(rowIndex); - } - - public void moveRow(int startIndex, int endIndex, int toIndex) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).moveRow(startIndex, endIndex, toIndex); - } - - public void insertRow(int rowIndex, Object[] rowData) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).insertRow(rowIndex, rowData); - } - - public void insertRow(int rowIndex, List rowData) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).insertRow(rowIndex, rowData.toArray()); - } - - public void setNumRows(int newSize) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).setNumRows(newSize); - } - - public void setDataVector(Object[][] newData, List columnIds) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).setDataVector( - newData, columnIds.toArray()); - } - - public void setDataVector(Object[][] newData, Object[] columnIds) { - checkDefaultTableModel(); - ((DefaultTableModel)getModel()).setDataVector(newData, columnIds); - } - - protected void checkDefaultTableModel() { - if(!(dataModel instanceof DefaultTableModel)) - throw new InternalError("In order to use this method, the data model must be an instance of DefaultTableModel."); - } - -// -// Methods removed from JTable in the move from identifiers to ints. -// - - public Object getValueAt(Object columnIdentifier, int rowIndex) { - return super.getValueAt(rowIndex, getColumnIndex(columnIdentifier)); - } - - public boolean isCellEditable(Object columnIdentifier, int rowIndex) { - return super.isCellEditable(rowIndex, getColumnIndex(columnIdentifier)); - } - - public void setValueAt(Object aValue, Object columnIdentifier, int rowIndex) { - super.setValueAt(aValue, rowIndex, getColumnIndex(columnIdentifier)); - } - - public boolean editColumnRow(Object identifier, int row) { - return super.editCellAt(row, getColumnIndex(identifier)); - } - - public void moveColumn(Object columnIdentifier, Object targetColumnIdentifier) { - moveColumn(getColumnIndex(columnIdentifier), - getColumnIndex(targetColumnIdentifier)); - } - - public boolean isColumnSelected(Object identifier) { - return isColumnSelected(getColumnIndex(identifier)); - } - - public TableColumn addColumn(int modelColumn, int width) { - return addColumn(modelColumn, width, null, null); - } - - public TableColumn addColumn(int modelColumn) { - return addColumn(modelColumn, 75, null, null); - } - - /** - * Creates a new column with modelColumn, width, - * renderer, and editor and adds it to the end of - * the JTable's array of columns. This method also retrieves the - * name of the column using the model's getColumnName(modelColumn) - * method, and sets the both the header value and the identifier - * for this TableColumn accordingly. - *

- * The modelColumn is the index of the column in the model which - * will supply the data for this column in the table. This, like the - * columnIdentifier in previous releases, does not change as the - * columns are moved in the view. - *

- * For the rest of the JTable API, and all of its associated classes, - * columns are referred to in the co-ordinate system of the view, the - * index of the column in the model is kept inside the TableColumn - * and is used only to retrieve the information from the appropraite - * column in the model. - *

- * - * @param modelColumn The index of the column in the model - * @param width The new column's width. Or -1 to use - * the default width - * @param renderer The renderer used with the new column. - * Or null to use the default renderer. - * @param editor The editor used with the new column. - * Or null to use the default editor. - */ - public TableColumn addColumn(int modelColumn, int width, - TableCellRenderer renderer, - TableCellEditor editor) { - TableColumn newColumn = new TableColumn( - modelColumn, width, renderer, editor); - addColumn(newColumn); - return newColumn; - } - -// -// Methods that had their arguments switched. -// - -// These won't work with the new table package. - -/* - public Object getValueAt(int columnIndex, int rowIndex) { - return super.getValueAt(rowIndex, columnIndex); - } - - public boolean isCellEditable(int columnIndex, int rowIndex) { - return super.isCellEditable(rowIndex, columnIndex); - } - - public void setValueAt(Object aValue, int columnIndex, int rowIndex) { - super.setValueAt(aValue, rowIndex, columnIndex); - } -*/ - - public boolean editColumnRow(int columnIndex, int rowIndex) { - return super.editCellAt(rowIndex, columnIndex); - } - - public boolean editColumnRow(int columnIndex, int rowIndex, EventObject e){ - return super.editCellAt(rowIndex, columnIndex, e); - } - - -} // End Of Class OldJTable From 175bbb143e9fd2e596eb234d46ef9259f2bc4c1a Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Thu, 29 Jan 2026 22:39:32 +0000 Subject: [PATCH 050/215] 8375569: Store Java mirrors in AOT configuration file Reviewed-by: iveresov, kvn, asmehra --- src/hotspot/share/cds/aotMappedHeapLoader.cpp | 10 +++--- src/hotspot/share/cds/aotMetaspace.cpp | 7 ++++- .../share/cds/aotReferenceObjSupport.cpp | 13 +++++--- src/hotspot/share/cds/cdsConfig.cpp | 31 +++++++++++++++++-- src/hotspot/share/cds/cdsConfig.hpp | 5 ++- src/hotspot/share/cds/heapShared.cpp | 26 ++++++++-------- src/hotspot/share/classfile/javaClasses.cpp | 4 +++ src/hotspot/share/classfile/stringTable.cpp | 22 +++++++++++++ src/hotspot/share/classfile/stringTable.hpp | 1 + .../cds/appcds/aotCache/AOTMapTest.java | 5 +-- 10 files changed, 96 insertions(+), 28 deletions(-) diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.cpp b/src/hotspot/share/cds/aotMappedHeapLoader.cpp index 146228436f4..210867be70c 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.cpp @@ -360,10 +360,8 @@ bool AOTMappedHeapLoader::load_heap_region(FileMapInfo* mapinfo) { } objArrayOop AOTMappedHeapLoader::root_segment(int segment_idx) { - if (CDSConfig::is_dumping_heap()) { - assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread"); - } else { - assert(CDSConfig::is_using_archive(), "must be"); + if (!CDSConfig::is_using_archive()) { + assert(CDSConfig::is_dumping_heap() && Thread::current() == (Thread*)VMThread::vm_thread(), "sanity"); } objArrayOop segment = (objArrayOop)_root_segments->at(segment_idx).resolve(); @@ -465,6 +463,10 @@ void AOTMappedHeapLoader::finish_initialization(FileMapInfo* info) { assert(segment_oop->is_objArray(), "Must be"); add_root_segment((objArrayOop)segment_oop); } + + if (CDSConfig::is_dumping_final_static_archive()) { + StringTable::move_shared_strings_into_runtime_table(); + } } } diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 62d76957c0a..894a35183ca 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1104,7 +1104,12 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS #if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_dumping_heap()) { - assert(CDSConfig::allow_only_single_java_thread(), "Required"); + if (!CDSConfig::is_dumping_preimage_static_archive()) { + // A single thread is required for Reference handling and deterministic CDS archive. + // Its's not required for dumping preimage, where References won't be archived and + // determinism is not needed. + assert(CDSConfig::allow_only_single_java_thread(), "Required"); + } if (!HeapShared::is_archived_boot_layer_available(THREAD)) { report_loading_error("archivedBootLayer not available, disabling full module graph"); CDSConfig::stop_dumping_full_module_graph(); diff --git a/src/hotspot/share/cds/aotReferenceObjSupport.cpp b/src/hotspot/share/cds/aotReferenceObjSupport.cpp index aa7cc875533..0c27c8ce5f0 100644 --- a/src/hotspot/share/cds/aotReferenceObjSupport.cpp +++ b/src/hotspot/share/cds/aotReferenceObjSupport.cpp @@ -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 @@ -177,12 +177,17 @@ void AOTReferenceObjSupport::init_keep_alive_objs_table() { // Returns true IFF obj is an instance of java.lang.ref.Reference. If so, perform extra eligibility checks. bool AOTReferenceObjSupport::check_if_ref_obj(oop obj) { - // We have a single Java thread. This means java.lang.ref.Reference$ReferenceHandler thread - // is not running. Otherwise the checks for next/discovered may not work. - precond(CDSConfig::allow_only_single_java_thread()); assert_at_safepoint(); // _keep_alive_objs_table uses raw oops if (obj->klass()->is_subclass_of(vmClasses::Reference_klass())) { + // The following check works only if the java.lang.ref.Reference$ReferenceHandler thread + // is not running. + // + // This code is called on every object found by AOTArtifactFinder. When dumping the + // preimage archive, AOTArtifactFinder should not find any Reference objects. + precond(!CDSConfig::is_dumping_preimage_static_archive()); + precond(CDSConfig::allow_only_single_java_thread()); + precond(AOTReferenceObjSupport::is_enabled()); precond(JavaClasses::is_supported_for_archiving(obj)); precond(_keep_alive_objs_table != nullptr); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 5f6b568dd6e..f4ef3c66f7a 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -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 @@ -556,7 +556,9 @@ void CDSConfig::check_aotmode_record() { // At VM exit, the module graph may be contaminated with program states. // We will rebuild the module graph when dumping the CDS final image. - disable_heap_dumping(); + _is_using_optimized_module_handling = false; + _is_using_full_module_graph = false; + _is_dumping_full_module_graph = false; } void CDSConfig::check_aotmode_create() { @@ -582,6 +584,7 @@ void CDSConfig::check_aotmode_create() { substitute_aot_filename(FLAG_MEMBER_ENUM(AOTCache)); _is_dumping_final_static_archive = true; + _is_using_full_module_graph = false; UseSharedSpaces = true; RequireSharedSpaces = true; @@ -954,7 +957,9 @@ bool CDSConfig::are_vm_options_incompatible_with_dumping_heap() { } bool CDSConfig::is_dumping_heap() { - if (!(is_dumping_classic_static_archive() || is_dumping_final_static_archive()) + // Note: when dumping preimage static archive, only a very limited set of oops + // are dumped. + if (!is_dumping_static_archive() || are_vm_options_incompatible_with_dumping_heap() || _disable_heap_dumping) { return false; @@ -966,6 +971,26 @@ bool CDSConfig::is_loading_heap() { return HeapShared::is_archived_heap_in_use(); } +bool CDSConfig::is_dumping_klass_subgraphs() { + if (is_dumping_classic_static_archive() || is_dumping_final_static_archive()) { + // KlassSubGraphs (see heapShared.cpp) is a legacy mechanism for archiving oops. It + // has been superceded by AOT class linking. This feature is used only when + // AOT class linking is disabled. + // + // KlassSubGraphs are disabled in the preimage static archive, which contains a very + // limited set of oops. + return is_dumping_heap() && !is_dumping_aot_linked_classes(); + } else { + return false; + } +} + +bool CDSConfig::is_using_klass_subgraphs() { + return (is_loading_heap() && + !CDSConfig::is_using_aot_linked_classes() && + !CDSConfig::is_dumping_final_static_archive()); +} + bool CDSConfig::is_using_full_module_graph() { if (ClassLoaderDataShared::is_full_module_graph_loaded()) { return true; diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index 202904e8231..739dbb4937b 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.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 @@ -188,6 +188,9 @@ public: static bool is_dumping_heap() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_loading_heap() NOT_CDS_JAVA_HEAP_RETURN_(false); + static bool is_dumping_klass_subgraphs() NOT_CDS_JAVA_HEAP_RETURN_(false); + static bool is_using_klass_subgraphs() NOT_CDS_JAVA_HEAP_RETURN_(false); + static bool is_dumping_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_dumping_method_handles() NOT_CDS_JAVA_HEAP_RETURN_(false); diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 42129011612..143f9147853 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -210,7 +210,7 @@ static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], Instan bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) { assert(CDSConfig::is_dumping_heap(), "dump-time only"); - if (!CDSConfig::is_dumping_aot_linked_classes()) { + if (CDSConfig::is_dumping_klass_subgraphs()) { // Legacy CDS archive support (to be deprecated) return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) || is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik); @@ -455,7 +455,6 @@ int HeapShared::append_root(oop obj) { oop HeapShared::get_root(int index, bool clear) { assert(index >= 0, "sanity"); - assert(!CDSConfig::is_dumping_heap() && CDSConfig::is_using_archive(), "runtime only"); assert(is_archived_heap_in_use(), "getting roots into heap that is not used"); oop result; @@ -600,8 +599,7 @@ public: void set_oop(MetaspaceObj* ptr, oop o) { MutexLocker ml(ScratchObjects_lock, Mutex::_no_safepoint_check_flag); OopHandle handle(Universe::vm_global(), o); - bool is_new = put(ptr, handle); - assert(is_new, "cannot set twice"); + put_when_absent(ptr, handle); } void remove_oop(MetaspaceObj* ptr) { MutexLocker ml(ScratchObjects_lock, Mutex::_no_safepoint_check_flag); @@ -614,6 +612,11 @@ public: }; void HeapShared::add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) { + if (CDSConfig::is_dumping_preimage_static_archive() && scratch_resolved_references(src) != nullptr) { + // We are in AOT training run. The class has been redefined and we are giving it a new resolved_reference. + // Ignore it, as this class will be excluded from the AOT config. + return; + } if (SystemDictionaryShared::is_builtin_loader(src->pool_holder()->class_loader_data())) { _scratch_objects_table->set_oop(src, dest); } @@ -934,7 +937,7 @@ void HeapShared::scan_java_class(Klass* orig_k) { void HeapShared::archive_subgraphs() { assert(CDSConfig::is_dumping_heap(), "must be"); - if (!CDSConfig::is_dumping_aot_linked_classes()) { + if (CDSConfig::is_dumping_klass_subgraphs()) { archive_object_subgraphs(archive_subgraph_entry_fields, false /* is_full_module_graph */); if (CDSConfig::is_dumping_full_module_graph()) { @@ -1292,10 +1295,7 @@ static void verify_the_heap(Klass* k, const char* which) { // this case, we will not load the ArchivedKlassSubGraphInfoRecord and will clear its roots. void HeapShared::resolve_classes(JavaThread* current) { assert(CDSConfig::is_using_archive(), "runtime only!"); - if (!is_archived_heap_in_use()) { - return; // nothing to do - } - if (!CDSConfig::is_using_aot_linked_classes()) { + if (CDSConfig::is_using_klass_subgraphs()) { resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields); resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields); } @@ -1385,7 +1385,7 @@ void HeapShared::init_classes_for_special_subgraph(Handle class_loader, TRAPS) { void HeapShared::initialize_from_archived_subgraph(JavaThread* current, Klass* k) { JavaThread* THREAD = current; - if (!is_archived_heap_in_use()) { + if (!CDSConfig::is_using_klass_subgraphs()) { return; // nothing to do } @@ -1861,7 +1861,7 @@ void HeapShared::archive_reachable_objects_from_static_field(InstanceKlass *k, const char* klass_name, int field_offset, const char* field_name) { - assert(CDSConfig::is_dumping_heap(), "dump time only"); + precond(CDSConfig::is_dumping_klass_subgraphs()); assert(k->defined_by_boot_loader(), "must be boot class"); oop m = k->java_mirror(); @@ -1912,7 +1912,7 @@ class VerifySharedOopClosure: public BasicOopIterateClosure { }; void HeapShared::verify_subgraph_from_static_field(InstanceKlass* k, int field_offset) { - assert(CDSConfig::is_dumping_heap(), "dump time only"); + precond(CDSConfig::is_dumping_klass_subgraphs()); assert(k->defined_by_boot_loader(), "must be boot class"); oop m = k->java_mirror(); @@ -2138,7 +2138,7 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], void HeapShared::init_subgraph_entry_fields(TRAPS) { assert(CDSConfig::is_dumping_heap(), "must be"); _dump_time_subgraph_info_table = new (mtClass)DumpTimeKlassSubGraphInfoTable(); - if (!CDSConfig::is_dumping_aot_linked_classes()) { + if (CDSConfig::is_dumping_klass_subgraphs()) { init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK); if (CDSConfig::is_dumping_full_module_graph()) { init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK); diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index dd70d7b49ab..b650bf8cfb8 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1263,6 +1263,10 @@ bool java_lang_Class::restore_archived_mirror(Klass *k, "Restored %s archived mirror " PTR_FORMAT, k->external_name(), p2i(mirror())); } + if (CDSConfig::is_dumping_heap()) { + create_scratch_mirror(k, CHECK_(false)); + } + return true; } #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/stringTable.cpp b/src/hotspot/share/classfile/stringTable.cpp index bbc12c8dcab..2b8b7780a41 100644 --- a/src/hotspot/share/classfile/stringTable.cpp +++ b/src/hotspot/share/classfile/stringTable.cpp @@ -987,4 +987,26 @@ void StringTable::serialize_shared_table_header(SerializeClosure* soc) { _shared_table.reset(); } } + +void StringTable::move_shared_strings_into_runtime_table() { + precond(CDSConfig::is_dumping_final_static_archive()); + JavaThread* THREAD = JavaThread::current(); + HandleMark hm(THREAD); + + int n = 0; + _shared_table.iterate_all([&](oop string) { + int length = java_lang_String::length(string); + Handle h_string (THREAD, string); + StringWrapper name(h_string, length); + unsigned int hash = hash_wrapped_string(name); + + assert(!_alt_hash, "too early"); + oop interned = do_intern(name, hash, THREAD); + assert(string == interned, "must be"); + n++; + }); + + _shared_table.reset(); + log_info(aot)("Moved %d interned strings to runtime table", n); +} #endif //INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/classfile/stringTable.hpp b/src/hotspot/share/classfile/stringTable.hpp index 94a0db5b5a5..0024a45a2f2 100644 --- a/src/hotspot/share/classfile/stringTable.hpp +++ b/src/hotspot/share/classfile/stringTable.hpp @@ -119,6 +119,7 @@ public: static void init_shared_table() NOT_CDS_JAVA_HEAP_RETURN; static void write_shared_table() NOT_CDS_JAVA_HEAP_RETURN; static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; + static void move_shared_strings_into_runtime_table(); // Jcmd static void dump(outputStream* st, bool verbose=false); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java index 6cbfcbbd3c3..209eb064945 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.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 @@ -136,6 +136,7 @@ public class AOTMapTest { } class AOTMapTestApp { + static URLClassLoader loader; // keep Hello class alive public static void main(String[] args) throws Exception { System.out.println("Hello AOTMapTestApp"); testCustomLoader(); @@ -144,7 +145,7 @@ class AOTMapTestApp { static void testCustomLoader() throws Exception { File custJar = new File("cust.jar"); URL[] urls = new URL[] {custJar.toURI().toURL()}; - URLClassLoader loader = new URLClassLoader(urls, AOTMapTestApp.class.getClassLoader()); + loader = new URLClassLoader(urls, AOTMapTestApp.class.getClassLoader()); Class c = loader.loadClass("Hello"); System.out.println(c); } From 379dcb0266bc90fac740eaa56b8027c7273e6d76 Mon Sep 17 00:00:00 2001 From: Alexander Zvegintsev Date: Fri, 30 Jan 2026 02:43:57 +0000 Subject: [PATCH 051/215] 8365313: GTK LaF does not respect system color scheme with Gnome Reviewed-by: prr, mkartashev, kizune --- .../java/swing/plaf/gtk/GTKLookAndFeel.java | 17 +++++++++ .../native/libawt_xawt/awt/gtk3_interface.c | 36 +++++++++++++++++++ .../native/libawt_xawt/awt/gtk3_interface.h | 5 +++ .../native/libawt_xawt/awt/gtk_interface.h | 2 ++ .../native/libawt_xawt/awt/swing_GTKEngine.c | 14 ++++++++ 5 files changed, 74 insertions(+) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java index 24a1997bdc1..5145779a493 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java @@ -1421,6 +1421,8 @@ public class GTKLookAndFeel extends SynthLookAndFeel { return c.getComponentOrientation().isLeftToRight(); } + private native boolean applyThemeIfNeeded(); + /** * {@inheritDoc} */ @@ -1463,6 +1465,21 @@ public class GTKLookAndFeel extends SynthLookAndFeel { // By default mnemonics are hidden for GTK L&F MnemonicHandler.setMnemonicHidden(true); + + if (IS_3) { + boolean shouldApplyTheme = false; + if (toolkit instanceof UNIXToolkit unixToolkit) { + if ("gnome".equals(unixToolkit.getDesktop())) { + int gnomeShellVersion = unixToolkit.getGnomeShellMajorVersion(); + shouldApplyTheme = gnomeShellVersion >= 47; + } + } + + if (shouldApplyTheme && applyThemeIfNeeded()) { + GTKEngine.INSTANCE.themeChanged(); + GTKIconFactory.resetIcons(); + } + } } /** 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 03dba969e8d..6ed23ca7541 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 @@ -602,6 +602,9 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) fp_g_uuid_string_is_valid = //since: 2.52 dl_symbol("g_uuid_string_is_valid"); fp_g_variant_print = dl_symbol("g_variant_print"); // since 2.24 + + fp_g_settings_new = dl_symbol("g_settings_new"); // since 2.26 + fp_g_settings_get_string = dl_symbol("g_settings_get_string"); // since 2.26 } fp_g_string_printf = dl_symbol("g_string_printf"); fp_g_strconcat = dl_symbol("g_strconcat"); @@ -2987,6 +2990,37 @@ static GdkWindow* gtk3_get_window(void *widget) { return fp_gtk_widget_get_window((GtkWidget*)widget); } +static gboolean apply_theme_if_needed() { + if (!glib_version_2_68) { + return FALSE; + } + + GSettings *settings = fp_g_settings_new("org.gnome.desktop.interface"); + if (!settings) { + return FALSE; + } + + static gboolean wasDark = FALSE; + + gchar *scheme = fp_g_settings_get_string(settings, "color-scheme"); + const gboolean isDark = strcmp(scheme, "prefer-dark") == 0; + + fp_g_free(scheme); + fp_g_object_unref(settings); + + if (wasDark ^ isDark) { + GtkSettings* gtkSettings = fp_gtk_settings_get_default(); + if (!gtkSettings) { + return FALSE; + } + fp_g_object_set(gtkSettings, "gtk-application-prefer-dark-theme", isDark, NULL); + wasDark = isDark; + return TRUE; + } + + return FALSE; +} + static void gtk3_init(GtkApi* gtk) { gtk->version = GTK_3; @@ -2996,6 +3030,8 @@ static void gtk3_init(GtkApi* gtk) { gtk->gtk_check_version = fp_gtk_check_version; gtk->get_setting = >k3_get_setting; + gtk->apply_theme_if_needed = &apply_theme_if_needed; + gtk->paint_arrow = >k3_paint_arrow; gtk->paint_box = >k3_paint_box; gtk->paint_box_gap = >k3_paint_box_gap; 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 d3a83677dd6..637a3198e43 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 @@ -193,6 +193,7 @@ typedef void GtkMenuShell; typedef void GtkWidgetClass; typedef void PangoFontDescription; typedef void GtkSettings; +typedef void GSettings; typedef void GtkStyleProvider; typedef void cairo_pattern_t; typedef void cairo_t; @@ -633,6 +634,10 @@ static char* (*fp_pango_font_description_to_string)( const PangoFontDescription* fd); static GtkSettings* (*fp_gtk_settings_get_default)(); static GtkSettings* (*fp_gtk_widget_get_settings)(GtkWidget *widget); + +static GSettings *(*fp_g_settings_new)(const gchar *schema_id); +static gchar *(*fp_g_settings_get_string)(GSettings *settings, const gchar *key); + static GType (*fp_gtk_border_get_type)(); static void (*fp_gtk_arrow_set)(GtkWidget* arrow, GtkArrowType arrow_type, 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 39be6a735d7..37422094202 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 @@ -541,6 +541,8 @@ typedef struct GtkApi { guint required_micro); jobject (*get_setting)(JNIEnv *env, Setting property); + gboolean (*apply_theme_if_needed)(); + void (*paint_arrow)(WidgetType widget_type, GtkStateType state_type, GtkShadowType shadow_type, const gchar *detail, gint x, gint y, gint width, gint height, diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c index bb5c799f008..3b7b2880316 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c @@ -388,3 +388,17 @@ Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeSetRangeValue( gtk->set_range_value(widget_type, value, min, max, visible); gtk->gdk_threads_leave(); } + +/* + * Class: com_sun_java_swing_plaf_gtk_GTKLookAndFeel + * Method: applyThemeIfNeeded + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL +Java_com_sun_java_swing_plaf_gtk_GTKLookAndFeel_applyThemeIfNeeded(JNIEnv *env, jobject this) { + gtk->gdk_threads_enter(); + const gboolean result = gtk->apply_theme_if_needed(); + gtk->gdk_threads_leave(); + + return result; +} From 9a10cceeafa5d332aa571f0d62acf50032a597d4 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Fri, 30 Jan 2026 03:19:49 +0000 Subject: [PATCH 052/215] 8374506: Incorrect positioning of arrow icon in parent JMenu in Windows L&F Reviewed-by: aivanov, kizune --- .../swing/plaf/windows/WindowsMenuItemUI.java | 11 ++- .../LargeMenuTextArrowIconPosition.java | 92 +++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/jdk/javax/swing/JMenuItem/LargeMenuTextArrowIconPosition.java diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 61f760d63c4..d15bc93a628 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -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 @@ -238,6 +238,15 @@ public final class WindowsMenuItemUI extends BasicMenuItemUI { SwingUtilities3.paintAccText(g, lh, lr, disabledForeground, acceleratorSelectionForeground, acceleratorForeground); + if (lh.getCheckIcon() != null && lh.useCheckAndArrow()) { + Rectangle rect = lr.getArrowRect(); + if (menuItem.getComponentOrientation().isLeftToRight()) { + rect.x += lh.getAfterCheckIconGap(); + } else { + rect.x -= lh.getAfterCheckIconGap(); + } + lr.setArrowRect(rect); + } SwingUtilities3.paintArrowIcon(g, lh, lr, foreground); // Restore original graphics font and color diff --git a/test/jdk/javax/swing/JMenuItem/LargeMenuTextArrowIconPosition.java b/test/jdk/javax/swing/JMenuItem/LargeMenuTextArrowIconPosition.java new file mode 100644 index 00000000000..72512560cff --- /dev/null +++ b/test/jdk/javax/swing/JMenuItem/LargeMenuTextArrowIconPosition.java @@ -0,0 +1,92 @@ +/* + * 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 8374506 + * @summary Verify if arrow icon positioning is correct in + * parent JMenu in Windows L&F + * @requires (os.family == "windows") + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual LargeMenuTextArrowIconPosition + */ + +import java.awt.BorderLayout; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.UIManager; + +public class LargeMenuTextArrowIconPosition { + + private static final String INSTRUCTIONS = """ + A frame will be shown with a label. + Right click on the label. + + Check the arrow icon at the end of + "Really long Menu-Text" text. + If it overlaps with the menu text, + press Fail else press Pass."""; + + public static void main(String[] args) throws Throwable { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + + PassFailJFrame.builder() + .instructions(INSTRUCTIONS) + .columns(40) + .testUI(LargeMenuTextArrowIconPosition::createTestUI) + .build() + .awaitAndCheck(); + } + + private static JFrame createTestUI() { + + JFrame frame = new JFrame("LargeMenuTextArrowIcon"); + frame.setSize(300, 150); + frame.setLayout(new BorderLayout()); + + JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.add(new JCheckBoxMenuItem("CheckBox On", true)); + popupMenu.add(new JCheckBoxMenuItem("CheckBox Icon On", + UIManager.getIcon("FileView.floppyDriveIcon"), true)); + popupMenu.add(new JCheckBoxMenuItem("CheckBox Icon Off", + UIManager.getIcon("FileView.floppyDriveIcon"), false)); + + JMenu menu = new JMenu("Really long Menu-Text"); + menu.add(new JMenuItem("Sub-MenuItem")); + menu.add(new JCheckBoxMenuItem("Sub-CheckBox On", true)); + + popupMenu.add(menu); + + JLabel lbl = new JLabel("Right click to invoke popupMenu"); + lbl.setComponentPopupMenu(popupMenu); + frame.add(lbl, BorderLayout.CENTER); + + return frame; + } + +} From 2953e0f445e147d778d4e765be0301cda6557ed5 Mon Sep 17 00:00:00 2001 From: Archie Cobbs Date: Fri, 30 Jan 2026 03:43:46 +0000 Subject: [PATCH 053/215] 8371162: Compiler warns about implicit cast from long to int in shift operation Reviewed-by: vromero --- .../com/sun/tools/javac/comp/Attr.java | 7 ++-- .../tools/javac/lint/AssignShift64Bits.java | 36 +++++++++++++++++++ .../tools/javac/lint/ShiftOutOfRange.out | 6 +--- 3 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 test/langtools/tools/javac/lint/AssignShift64Bits.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index cc21113882f..f2f9ea24ad7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.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 @@ -4000,7 +4000,10 @@ public class Attr extends JCTree.Visitor { chk.checkCastable(tree.rhs.pos(), operator.type.getReturnType(), owntype); - chk.checkLossOfPrecision(tree.rhs.pos(), operand, owntype); + switch (tree.getTag()) { + case SL_ASG, SR_ASG, USR_ASG -> { } // we only use (at most) the lower 6 bits, so any integral type is OK + default -> chk.checkLossOfPrecision(tree.rhs.pos(), operand, owntype); + } chk.checkOutOfRangeShift(tree.rhs.pos(), operator, operand); } result = check(tree, owntype, KindSelector.VAL, resultInfo); diff --git a/test/langtools/tools/javac/lint/AssignShift64Bits.java b/test/langtools/tools/javac/lint/AssignShift64Bits.java new file mode 100644 index 00000000000..bb889d09022 --- /dev/null +++ b/test/langtools/tools/javac/lint/AssignShift64Bits.java @@ -0,0 +1,36 @@ +/* + * 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 8371162 + * @summary Verify no lossy conversion warning for 64 bit shift amount + * @compile -Xlint:lossy-conversions -Werror AssignShift64Bits.java + */ + +public class AssignShift64Bits { + void m() { + int a = 1 << 1L; + a <<= 1L; + long b = 1 << 1L; + b <<= 1L; + } +} diff --git a/test/langtools/tools/javac/lint/ShiftOutOfRange.out b/test/langtools/tools/javac/lint/ShiftOutOfRange.out index 7aee0b014af..6942e7e6809 100644 --- a/test/langtools/tools/javac/lint/ShiftOutOfRange.out +++ b/test/langtools/tools/javac/lint/ShiftOutOfRange.out @@ -1,17 +1,13 @@ ShiftOutOfRange.java:14:18: compiler.warn.bit.shift.out.of.range: int, -32, 0 ShiftOutOfRange.java:15:18: compiler.warn.bit.shift.out.of.range: int, -32, 0 ShiftOutOfRange.java:16:19: compiler.warn.bit.shift.out.of.range: int, -32, 0 -ShiftOutOfRange.java:17:15: compiler.warn.possible.loss.of.precision: long, int ShiftOutOfRange.java:17:15: compiler.warn.bit.shift.out.of.range: int, -32, 0 ShiftOutOfRange.java:18:15: compiler.warn.bit.shift.out.of.range: int, -32, 0 ShiftOutOfRange.java:19:16: compiler.warn.bit.shift.out.of.range: int, -32, 0 -ShiftOutOfRange.java:25:15: compiler.warn.possible.loss.of.precision: long, int -ShiftOutOfRange.java:32:15: compiler.warn.possible.loss.of.precision: long, int ShiftOutOfRange.java:39:18: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:40:18: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:41:19: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:42:15: compiler.warn.bit.shift.out.of.range: int, 32, 0 -ShiftOutOfRange.java:43:15: compiler.warn.possible.loss.of.precision: long, int ShiftOutOfRange.java:43:15: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:44:16: compiler.warn.bit.shift.out.of.range: int, 32, 0 ShiftOutOfRange.java:51:18: compiler.warn.bit.shift.out.of.range: long, -64, 0 @@ -26,4 +22,4 @@ ShiftOutOfRange.java:78:19: compiler.warn.bit.shift.out.of.range: long, 64, 0 ShiftOutOfRange.java:79:15: compiler.warn.bit.shift.out.of.range: long, 64, 0 ShiftOutOfRange.java:80:15: compiler.warn.bit.shift.out.of.range: long, 64, 0 ShiftOutOfRange.java:81:16: compiler.warn.bit.shift.out.of.range: long, 64, 0 -28 warnings +24 warnings From 9fef14a6d3124fae3ad8b24dac5103aa611d4edb Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 30 Jan 2026 06:15:19 +0000 Subject: [PATCH 054/215] 8375571: Compiler crash when using record pattern matching with a generic type parameter shadowing a record class Reviewed-by: vromero --- .../com/sun/tools/javac/comp/Attr.java | 2 +- .../patterns/DeconstructionPatternErrors.java | 5 +++ .../patterns/DeconstructionPatternErrors.out | 45 ++++++++++--------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index f2f9ea24ad7..5109dfed22e 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4269,7 +4269,7 @@ public class Attr extends JCTree.Visitor { } List expectedRecordTypes; - if (site.tsym.kind == Kind.TYP && ((ClassSymbol) site.tsym).isRecord()) { + if (site.tsym instanceof ClassSymbol clazz && clazz.isRecord()) { ClassSymbol record = (ClassSymbol) site.tsym; expectedRecordTypes = record.getRecordComponents() .stream() diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java index 4e1a7d7f669..804c2a3f622 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java +++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.java @@ -1,5 +1,6 @@ /** * @test /nodynamiccopyright/ + * @bug 8375571 * @summary Verify error reports for erroneous deconstruction patterns are sensible * @compile/fail/ref=DeconstructionPatternErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDdev DeconstructionPatternErrors.java */ @@ -39,6 +40,10 @@ public class DeconstructionPatternErrors { boolean b = p instanceof P(int i) p; //introducing a variable for the record pattern } + void typeVarTest(T p) { + if (p instanceof T(int i) && i == 0); //T is a type variable + } + public record P(int i) { } diff --git a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out index f947142cd66..4d21d6069d8 100644 --- a/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out +++ b/test/langtools/tools/javac/patterns/DeconstructionPatternErrors.out @@ -1,23 +1,24 @@ -DeconstructionPatternErrors.java:35:37: compiler.err.illegal.start.of.type -DeconstructionPatternErrors.java:37:28: compiler.err.illegal.start.of.type -DeconstructionPatternErrors.java:39:42: compiler.err.expected: ';' -DeconstructionPatternErrors.java:39:43: compiler.err.not.stmt -DeconstructionPatternErrors.java:15:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List, java.util.ArrayList) -DeconstructionPatternErrors.java:16:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList -DeconstructionPatternErrors.java:17:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int) -DeconstructionPatternErrors.java:18:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String) -DeconstructionPatternErrors.java:19:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, DeconstructionPatternErrors.P) -DeconstructionPatternErrors.java:20:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable +DeconstructionPatternErrors.java:36:37: compiler.err.illegal.start.of.type +DeconstructionPatternErrors.java:38:28: compiler.err.illegal.start.of.type +DeconstructionPatternErrors.java:40:42: compiler.err.expected: ';' +DeconstructionPatternErrors.java:40:43: compiler.err.not.stmt +DeconstructionPatternErrors.java:16:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.util.List, java.util.ArrayList) +DeconstructionPatternErrors.java:17:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, java.util.ArrayList +DeconstructionPatternErrors.java:18:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, int) +DeconstructionPatternErrors.java:19:28: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: int, java.lang.String) +DeconstructionPatternErrors.java:20:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, DeconstructionPatternErrors.P) DeconstructionPatternErrors.java:21:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable -DeconstructionPatternErrors.java:22:26: compiler.err.incorrect.number.of.nested.patterns: int, int,compiler.misc.type.none -DeconstructionPatternErrors.java:23:26: compiler.err.incorrect.number.of.nested.patterns: int, int,int -DeconstructionPatternErrors.java:24:36: compiler.err.cant.resolve.location: kindname.class, Unresolvable, , , (compiler.misc.location: kindname.class, DeconstructionPatternErrors, null) -DeconstructionPatternErrors.java:24:26: compiler.err.incorrect.number.of.nested.patterns: int, int,Unresolvable -DeconstructionPatternErrors.java:25:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:26:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:27:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer) -DeconstructionPatternErrors.java:27:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord -DeconstructionPatternErrors.java:28:40: compiler.err.match.binding.exists -DeconstructionPatternErrors.java:29:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth() -DeconstructionPatternErrors.java:29:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth() -22 errors \ No newline at end of file +DeconstructionPatternErrors.java:22:26: compiler.err.incorrect.number.of.nested.patterns: java.lang.Runnable,java.lang.Runnable, java.lang.Runnable +DeconstructionPatternErrors.java:23:26: compiler.err.incorrect.number.of.nested.patterns: int, int,compiler.misc.type.none +DeconstructionPatternErrors.java:24:26: compiler.err.incorrect.number.of.nested.patterns: int, int,int +DeconstructionPatternErrors.java:25:36: compiler.err.cant.resolve.location: kindname.class, Unresolvable, , , (compiler.misc.location: kindname.class, DeconstructionPatternErrors, null) +DeconstructionPatternErrors.java:25:26: compiler.err.incorrect.number.of.nested.patterns: int, int,Unresolvable +DeconstructionPatternErrors.java:26:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:27:29: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:28:44: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String, java.lang.Integer) +DeconstructionPatternErrors.java:28:13: compiler.err.instanceof.reifiable.not.safe: java.lang.Object, DeconstructionPatternErrors.GenRecord +DeconstructionPatternErrors.java:29:40: compiler.err.match.binding.exists +DeconstructionPatternErrors.java:30:56: compiler.err.already.defined: kindname.variable, v1, kindname.method, meth() +DeconstructionPatternErrors.java:30:64: compiler.err.already.defined: kindname.variable, v2, kindname.method, meth() +DeconstructionPatternErrors.java:44:26: compiler.err.deconstruction.pattern.only.records: T +23 errors From 55375e98ae1672badeacaaf2f8b6f2f21ad03437 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Fri, 30 Jan 2026 08:31:27 +0000 Subject: [PATCH 055/215] 8375573: JTable ignores setPreferredWidth during initial layout when AUTO_RESIZE_LAST_COLUMN is enabled Reviewed-by: tr --- .../share/classes/javax/swing/JTable.java | 18 +++- .../swing/JTable/TestJTableColWidth.java | 87 +++++++++++++++++++ 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 test/jdk/javax/swing/JTable/TestJTableColWidth.java diff --git a/src/java.desktop/share/classes/javax/swing/JTable.java b/src/java.desktop/share/classes/javax/swing/JTable.java index 6e64b216d58..f5c914135d1 100644 --- a/src/java.desktop/share/classes/javax/swing/JTable.java +++ b/src/java.desktop/share/classes/javax/swing/JTable.java @@ -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 @@ -3191,9 +3191,23 @@ public class JTable extends JComponent implements TableModelListener, Scrollable * (maximum or minimum). * */ + public void doLayout() { + boolean prefWidthSet = false; TableColumn resizingColumn = getResizingColumn(); - if (resizingColumn == null) { + // doLayout is called for both pack and show + // so if initial preferred width is set by user then + // it needs to be honoured even if resizingColumn + // is set to last column on account of + // AUTO_RESIZE_LAST_COLUMN autoResizeMode + for (int i = 0; i < columnModel.getColumnCount(); i++) { + if (columnModel.getColumn(i).getPreferredWidth() != 75 + && columnModel.getColumn(i).getWidth() == 75) { + prefWidthSet = true; + break; + } + } + if (resizingColumn == null || prefWidthSet) { setWidthsFromPreferredWidths(false); } else { diff --git a/test/jdk/javax/swing/JTable/TestJTableColWidth.java b/test/jdk/javax/swing/JTable/TestJTableColWidth.java new file mode 100644 index 00000000000..542c4d0ffa1 --- /dev/null +++ b/test/jdk/javax/swing/JTable/TestJTableColWidth.java @@ -0,0 +1,87 @@ +/* + * 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 8192888 + * @key headful + * @summary Verifies JTable doesn't ignore setPreferredWidth during + * initial layout when AUTO_RESIZE_LAST_COLUMN is enabled + * @run main TestJTableColWidth + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumnModel; +import javax.swing.SwingUtilities; + +public class TestJTableColWidth { + static JFrame frame; + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeAndWait(() -> { + try { + frame = new JFrame("JTable colwidth"); + + String[] cols = {"ID", "Name", "Description", "Status"}; + Object[][] data = {{1, "Mimi", "Testing Java 25 Regression", "Pending"}}; + + DefaultTableModel model = new DefaultTableModel(data, cols); + final JTable tab = new JTable(model); + + tab.getTableHeader().setReorderingAllowed(false); + tab.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + + TableColumnModel columnModel = tab.getColumnModel(); + // Defined widths + int[] widths = {30, 200, 100, 50}; + + for (int i = 0; i < widths.length; i++) { + columnModel.getColumn(i).setPreferredWidth(widths[i]); + } + + frame.add(new JScrollPane(tab)); + frame.setSize(600, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + + System.out.println("Actual column widths on screen:"); + for (int i = 0; i < columnModel.getColumnCount(); i++) { + System.out.println("Column " + i + ": " + + columnModel.getColumn(i).getWidth() + "px"); + } + if (columnModel.getColumn(0).getWidth() + == columnModel.getColumn(1).getWidth()) { + throw new RuntimeException("JTable ignores setPreferredWidth during" + + " initial layout when AUTO_RESIZE_LAST_COLUMN is enabled"); + } + } finally { + if (frame != null) { + frame.dispose(); + } + } + }); + } +} From e6437264d5e6d4aad23430b7dbdf574a12b8f57b Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 30 Jan 2026 08:31:51 +0000 Subject: [PATCH 056/215] 8376604: C2: EA should assert is_oop_field for AddP with oop outs Reviewed-by: qamai, kvn --- src/hotspot/share/opto/escape.cpp | 21 ++++++++++++--------- src/hotspot/share/opto/escape.hpp | 3 ++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 357c91e8eb5..b46a12fcf89 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -3511,10 +3511,7 @@ bool ConnectionGraph::is_oop_field(Node* n, int offset, bool* unsafe) { bt = field->layout_type(); } else { // Check for unsafe oop field access - if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) || - n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) || - n->has_out_with(Op_CompareAndSwapP, Op_CompareAndSwapN, Op_WeakCompareAndSwapP, Op_WeakCompareAndSwapN) || - BarrierSet::barrier_set()->barrier_set_c2()->escape_has_out_with_unsafe_object(n)) { + if (has_oop_node_outs(n)) { bt = T_OBJECT; (*unsafe) = true; } @@ -3530,16 +3527,22 @@ bool ConnectionGraph::is_oop_field(Node* n, int offset, bool* unsafe) { } } else if (adr_type->isa_rawptr() || adr_type->isa_klassptr()) { // Allocation initialization, ThreadLocal field access, unsafe access - if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) || - n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) || - n->has_out_with(Op_CompareAndSwapP, Op_CompareAndSwapN, Op_WeakCompareAndSwapP, Op_WeakCompareAndSwapN) || - BarrierSet::barrier_set()->barrier_set_c2()->escape_has_out_with_unsafe_object(n)) { + if (has_oop_node_outs(n)) { bt = T_OBJECT; } } } // Note: T_NARROWOOP is not classed as a real reference type - return (is_reference_type(bt) || bt == T_NARROWOOP); + bool res = (is_reference_type(bt) || bt == T_NARROWOOP); + assert(!has_oop_node_outs(n) || res, "sanity: AddP has oop outs, needs to be treated as oop field"); + return res; +} + +bool ConnectionGraph::has_oop_node_outs(Node* n) { + return n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) || + n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) || + n->has_out_with(Op_CompareAndSwapP, Op_CompareAndSwapN, Op_WeakCompareAndSwapP, Op_WeakCompareAndSwapN) || + BarrierSet::barrier_set()->barrier_set_c2()->escape_has_out_with_unsafe_object(n); } // Returns unique pointed java object or null. diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index 04a9dc82982..dcb832889ec 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -539,7 +539,8 @@ private: } // Helper functions - bool is_oop_field(Node* n, int offset, bool* unsafe); + bool is_oop_field(Node* n, int offset, bool* unsafe); + bool has_oop_node_outs(Node* n); static Node* find_second_addp(Node* addp, Node* n); // offset of a field reference int address_offset(Node* adr, PhaseValues* phase); From 42370e22c5bc4ebd40fd500a2e6e9e07f0b8bcd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20H=C3=A4ssig?= Date: Fri, 30 Jan 2026 09:01:00 +0000 Subject: [PATCH 057/215] 8376781: Problemlist compiler/longcountedloops/TestLoopNestTooManyTraps.java Reviewed-by: thartmann, chagedorn --- test/hotspot/jtreg/ProblemList.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 84520b00056..7e521279a4c 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -77,6 +77,8 @@ compiler/interpreter/Test6833129.java 8335266 generic-i586 compiler/c2/aarch64/TestStaticCallStub.java 8359963 linux-aarch64,macosx-aarch64 +compiler/longcountedloops/TestLoopNestTooManyTraps.java 8376591 generic-all + ############################################################################# # :hotspot_gc From e3b5b261af6acbe7ab074f301c70283b06c17d39 Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Fri, 30 Jan 2026 09:35:32 +0000 Subject: [PATCH 058/215] 8376287: Crashes when using -XX:ObjArrayMarkingStride=0 Reviewed-by: tschatzl, shade --- src/hotspot/share/gc/shared/gc_globals.hpp | 1 + src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index d08e95378f7..6aa1fcf066b 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -261,6 +261,7 @@ develop(uintx, ObjArrayMarkingStride, 2048, \ "Number of object array elements to push onto the marking stack " \ "before pushing a continuation entry") \ + range(1, INT_MAX/2) \ \ product_pd(bool, NeverActAsServerClassMachine, \ "(Deprecated) Never act like a server-class machine") \ diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 849459157b5..ba24e890769 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -226,8 +226,6 @@ inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); - assert (ObjArrayMarkingStride > 0, "sanity"); - // Split out tasks, as suggested in ShenandoahMarkTask docs. Avoid pushing tasks that // are known to start beyond the array. while ((1 << pow) > (int)ObjArrayMarkingStride && (chunk*2 < ShenandoahMarkTask::chunk_size())) { From 0a3809d380bcae8cb24d50886057d8586fa77f7c Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 30 Jan 2026 11:33:03 +0000 Subject: [PATCH 059/215] 8375046: C2: Incremental inlining step asserts when processing empty late inlines list Reviewed-by: vlivanov, thartmann, kbarrett --- src/hotspot/share/opto/compile.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index eaec1605108..c4c9445b61a 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2098,6 +2098,7 @@ void Compile::inline_boxing_calls(PhaseIterGVN& igvn) { bool Compile::inline_incrementally_one() { assert(IncrementalInline, "incremental inlining should be on"); + assert(_late_inlines.length() > 0, "should have been checked by caller"); TracePhase tp(_t_incrInline_inline); @@ -2209,6 +2210,10 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { igvn_worklist()->ensure_empty(); // should be done with igvn + if (_late_inlines.length() == 0) { + break; // no more progress + } + while (inline_incrementally_one()) { assert(!failing_internal() || failure_is_artificial(), "inconsistent"); } @@ -2219,10 +2224,6 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { print_method(PHASE_INCREMENTAL_INLINE_STEP, 3); if (failing()) return; - - if (_late_inlines.length() == 0) { - break; // no more progress - } } igvn_worklist()->ensure_empty(); // should be done with igvn From df8c4d6d12dacd0adfcf8c711c8671913d805309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?= Date: Fri, 30 Jan 2026 13:44:48 +0000 Subject: [PATCH 060/215] 8373604: Operations on peer reset tokens are slow Reviewed-by: dfuchs --- .../net/http/quic/PeerConnIdManager.java | 25 ++++++++++- .../net/http/quic/QuicConnectionImpl.java | 11 +++-- .../internal/net/http/quic/QuicEndpoint.java | 41 +++++++++++-------- .../net/http/quic/QuicPacketReceiver.java | 7 +++- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java index 065d045b57c..2bc759a920a 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PeerConnIdManager.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 @@ -28,6 +28,7 @@ import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; @@ -276,6 +277,28 @@ final class PeerConnIdManager { } } + /** + * {@return the list of stateless reset tokens associated with active peer connection IDs} + */ + public List activeResetTokens() { + lock.lock(); + try { + // we only support one active connection ID at the time + PeerConnectionId cid = peerConnectionIds.get(activeConnIdSeq); + byte[] statelessResetToken = null; + if (cid != null) { + statelessResetToken = cid.getStatelessResetToken(); + } + if (statelessResetToken != null) { + return List.of(statelessResetToken); + } else { + return List.of(); + } + } finally { + lock.unlock(); + } + } + /** * {@return the active peer connection ID} */ diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java index 580bbe23d31..edb94d5929a 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicConnectionImpl.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 @@ -1246,7 +1246,7 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece final PacketSpace space = packetSpace(PacketNumberSpace.APPLICATION); final int maxDatagramSize = getMaxDatagramSize(); final QuicConnectionId peerConnectionId = peerConnectionId(); - final int dstIdLength = peerConnectionId().length(); + final int dstIdLength = peerConnectionId.length(); if (!canSend()) { return false; } @@ -1747,6 +1747,11 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece return localConnIdManager.connectionIds(); } + @Override + public List activeResetTokens() { + return peerConnIdManager.activeResetTokens(); + } + LocalConnIdManager localConnectionIdManager() { return localConnIdManager; } @@ -2453,7 +2458,7 @@ public class QuicConnectionImpl extends QuicConnection implements QuicPacketRece } return; } - final QuicConnectionId currentPeerConnId = this.peerConnIdManager.getPeerConnId(); + final QuicConnectionId currentPeerConnId = peerConnectionId(); if (rt.sourceId().equals(currentPeerConnId)) { if (debug.on()) { debug.log("Invalid retry, same connection ID"); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java index 91b4a678a23..ef342d4cb56 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.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 @@ -1572,14 +1572,14 @@ public abstract sealed class QuicEndpoint implements AutoCloseable private void dropPeerIssuedResetTokensFor(QuicPacketReceiver connection) { // remove references to this connection from the map which holds the peer issued // reset tokens - peerIssuedResetTokens.values().removeIf(conn -> connection == conn); + connection.activeResetTokens().forEach(this::forgetStatelessResetToken); } - // remap peer issued stateless token from connection `from` to connection `to` - private void remapPeerIssuedResetToken(QuicPacketReceiver from, QuicPacketReceiver to) { - assert from != null; - assert to != null; - peerIssuedResetTokens.replaceAll((tok, c) -> c == from ? to : c); + // remap peer issued stateless tokens to connection `newReceiver` + private void remapPeerIssuedResetToken(QuicPacketReceiver newReceiver) { + assert newReceiver != null; + newReceiver.activeResetTokens().forEach(resetToken -> + associateStatelessResetToken(resetToken, newReceiver)); } public void draining(final QuicConnectionImpl connection) { @@ -1588,9 +1588,10 @@ public abstract sealed class QuicEndpoint implements AutoCloseable final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO connection.localConnectionIdManager().close(); - DrainingConnection draining = new DrainingConnection(connection.connectionIds(), idleTimeout); + DrainingConnection draining = new DrainingConnection(connection.connectionIds(), + connection.activeResetTokens(), idleTimeout); // we can ignore stateless reset in the draining state. - remapPeerIssuedResetToken(connection, draining); + remapPeerIssuedResetToken(draining); connection.connectionIds().forEach((id) -> connections.compute(id, (i, r) -> remapDraining(i, r, draining))); @@ -1626,8 +1627,9 @@ public abstract sealed class QuicEndpoint implements AutoCloseable final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO connection.localConnectionIdManager().close(); - var closingConnection = new ClosingConnection(connection.connectionIds(), idleTimeout, datagram); - remapPeerIssuedResetToken(connection, closingConnection); + var closingConnection = new ClosingConnection(connection.connectionIds(), + connection.activeResetTokens(), idleTimeout, datagram); + remapPeerIssuedResetToken(closingConnection); connection.connectionIds().forEach((id) -> connections.compute(id, (i, r) -> remapClosing(i, r, closingConnection))); @@ -1745,6 +1747,7 @@ public abstract sealed class QuicEndpoint implements AutoCloseable // an instance of this class) final static long NO_IDLE_TIMEOUT = 2000; final List localConnectionIds; + private final List activeResetTokens; final long maxIdleTimeMs; final long id; int more = 1; @@ -1752,7 +1755,8 @@ public abstract sealed class QuicEndpoint implements AutoCloseable volatile Deadline deadline; volatile Deadline updatedDeadline; - ClosedConnection(List localConnectionIds, long maxIdleTimeMs) { + ClosedConnection(List localConnectionIds, List activeResetTokens, long maxIdleTimeMs) { + this.activeResetTokens = activeResetTokens; this.id = QuicTimerQueue.newEventId(); this.maxIdleTimeMs = maxIdleTimeMs == 0 ? NO_IDLE_TIMEOUT : maxIdleTimeMs; this.deadline = Deadline.MAX; @@ -1765,6 +1769,11 @@ public abstract sealed class QuicEndpoint implements AutoCloseable return localConnectionIds; } + @Override + public List activeResetTokens() { + return activeResetTokens; + } + @Override public final void processIncoming(SocketAddress source, ByteBuffer destConnId, HeadersType headersType, ByteBuffer buffer) { Deadline updated = updatedDeadline; @@ -1868,9 +1877,9 @@ public abstract sealed class QuicEndpoint implements AutoCloseable final ByteBuffer closePacket; - ClosingConnection(List localConnIdManager, long maxIdleTimeMs, + ClosingConnection(List localConnectionIds, List activeResetTokens, long maxIdleTimeMs, ByteBuffer closePacket) { - super(localConnIdManager, maxIdleTimeMs); + super(localConnectionIds, activeResetTokens, maxIdleTimeMs); this.closePacket = Objects.requireNonNull(closePacket); } @@ -1903,8 +1912,8 @@ public abstract sealed class QuicEndpoint implements AutoCloseable */ final class DrainingConnection extends ClosedConnection { - DrainingConnection(List localConnIdManager, long maxIdleTimeMs) { - super(localConnIdManager, maxIdleTimeMs); + DrainingConnection(List localConnectionIds, List activeResetTokens, long maxIdleTimeMs) { + super(localConnectionIds, activeResetTokens, maxIdleTimeMs); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacketReceiver.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacketReceiver.java index 6652b44de3a..72f622731f3 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacketReceiver.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicPacketReceiver.java @@ -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 @@ -51,6 +51,11 @@ public interface QuicPacketReceiver { */ List connectionIds(); + /** + * {@return a list of active peer stateless reset tokens for this connection) + */ + List activeResetTokens(); + /** * {@return the initial connection id assigned by the peer} * On the client side, this is always {@link Optional#empty()}. From 96180b9c56a03f6d7cb22c0618ed7d946beae6bf Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Fri, 30 Jan 2026 15:44:51 +0000 Subject: [PATCH 061/215] 8376308: java/net/httpclient/CancelRequestTest.java fails intermittently with "Expected CancellationException not received" Reviewed-by: djelinski, vyazici --- .../net/httpclient/CancelRequestTest.java | 131 ++++++++++++------ 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/test/jdk/java/net/httpclient/CancelRequestTest.java b/test/jdk/java/net/httpclient/CancelRequestTest.java index 86e06bb78f7..2a0ec19a0ed 100644 --- a/test/jdk/java/net/httpclient/CancelRequestTest.java +++ b/test/jdk/java/net/httpclient/CancelRequestTest.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 @@ -49,7 +49,6 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -65,6 +64,7 @@ import java.net.http.HttpResponse.BodyHandlers; import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Random; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -87,11 +87,14 @@ import static java.net.http.HttpOption.H3_DISCOVERY; 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.assertNotEquals; import static org.testng.Assert.assertTrue; public class CancelRequestTest implements HttpServerAdapters { private static final Random random = RandomFactory.getRandom(); + private static final ConcurrentHashMap latches + = new ConcurrentHashMap<>(); private static final SSLContext sslContext = SimpleSSLContext.findSSLContext(); HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] @@ -117,6 +120,7 @@ public class CancelRequestTest implements HttpServerAdapters { static volatile boolean tasksFailed; static final AtomicLong serverCount = new AtomicLong(); static final AtomicLong clientCount = new AtomicLong(); + static final AtomicLong requestCount = new AtomicLong(); static final long start = System.nanoTime(); public static String now() { long now = System.nanoTime() - start; @@ -278,10 +282,12 @@ public class CancelRequestTest implements HttpServerAdapters { // set HTTP/3 version on the request when targeting // an HTTP/3 server private HttpRequest.Builder requestBuilder(String uri) { - var builder = HttpRequest.newBuilder(URI.create(uri)); + var u = URI.create(uri+"?req="+requestCount.incrementAndGet()); + var builder = HttpRequest.newBuilder(u); if (uri.contains("h3")) { builder.version(HTTP_3); } + builder.setHeader("X-expect-exception", "true"); return builder; } @@ -324,6 +330,24 @@ public class CancelRequestTest implements HttpServerAdapters { assertEquals(resp.statusCode(), 200); } + private static void releaseLatches() { + // release left over latches + for (var latch : latches.values()) { + latch.countDown(); + } + latches.clear(); + } + + private static CountDownLatch addLatchFor(HttpRequest req) { + // release left over latches + releaseLatches(); + String key = Objects.requireNonNull(req.uri().getRawQuery(), "query"); + var latch = new CountDownLatch(1); + latches.put(key, latch); + out.println(now() + "CountDownLatch " + latch + " added for " + req.uri()); + return latch; + } + @Test(dataProvider = "asyncurls") public void testGetSendAsync(String uri, boolean sameClient, boolean mayInterruptIfRunning) throws Exception { @@ -343,18 +367,21 @@ public class CancelRequestTest implements HttpServerAdapters { .GET() .setOption(H3_DISCOVERY, config) .build(); + var requestLatch = addLatchFor(req); + BodyHandler handler = BodyHandlers.ofString(); CountDownLatch latch = new CountDownLatch(1); + out.println(now() + "Sending (async): " + req.uri()); CompletableFuture> response = client.sendAsync(req, handler); var cf1 = response.whenComplete((r,t) -> System.out.println(t)); CompletableFuture> cf2 = cf1.whenComplete((r,t) -> latch.countDown()); - out.println("iteration: " + i + ", req: " + req.uri()); + out.println(now() + "iteration: " + i + ", req sent: " + req.uri()); out.println("response: " + response); out.println("cf1: " + cf1); out.println("cf2: " + cf2); delay(); cf1.cancel(mayInterruptIfRunning); - out.println("response after cancel: " + response); + out.println(now() + "response after cancel: " + response); out.println("cf1 after cancel: " + cf1); out.println("cf2 after cancel: " + cf2); try { @@ -362,8 +389,10 @@ public class CancelRequestTest implements HttpServerAdapters { assertEquals(body, String.join("", BODY.split("\\|"))); throw new AssertionError("Expected CancellationException not received"); } catch (ExecutionException x) { - out.println("Got expected exception: " + x); + out.println(now() + "Got expected exception: " + x); assertTrue(isCancelled(x)); + } finally { + requestLatch.countDown(); } // Cancelling the request may cause an IOException instead... @@ -371,7 +400,7 @@ public class CancelRequestTest implements HttpServerAdapters { try { cf1.get(); } catch (CancellationException | ExecutionException x) { - out.println("Got expected exception: " + x); + out.println(now() + "Got expected exception: " + x); assertTrue(isCancelled(x)); hasCancellationException = x instanceof CancellationException; } @@ -393,7 +422,7 @@ public class CancelRequestTest implements HttpServerAdapters { if (mayInterruptIfRunning) { if (CancellationException.class.isAssignableFrom(wrapped.getClass())) { cause = wrapped.getCause(); - out.println("CancellationException cause: " + x); + out.println(now() + "CancellationException cause: " + x); } else if (!isCancelled(cause)) { throw new RuntimeException("Unexpected cause: " + cause); } @@ -403,15 +432,15 @@ public class CancelRequestTest implements HttpServerAdapters { } } if (!IOException.class.isInstance(cause)) { - out.println("Unexpected cause: " + cause.getClass()); + out.println(now() + "Unexpected cause: " + cause.getClass()); cause.printStackTrace(out); } assertTrue(IOException.class.isAssignableFrom(cause.getClass())); if (mayInterruptIfRunning) { - out.println("Got expected exception: " + wrapped); + out.println(now() + "Got expected exception: " + wrapped); out.println("\tcause: " + cause); } else { - out.println("Unexpected exception: " + wrapped); + out.println(now() + "Unexpected exception: " + wrapped); wrapped.printStackTrace(out); throw x; } @@ -453,13 +482,13 @@ public class CancelRequestTest implements HttpServerAdapters { @Override public Iterator iterator() { // this is dangerous - out.println("waiting for completion on: " + cancelFuture); + out.println(now() + "waiting for completion on: " + cancelFuture); boolean async = random.nextBoolean(); Runnable cancel = () -> { - out.println("Cancelling from " + Thread.currentThread()); + out.println(now() + "Cancelling from " + Thread.currentThread()); var cf1 = cancelFuture.join(); cf1.cancel(mayInterruptIfRunning); - out.println("cancelled " + cf1); + out.println(now() + "cancelled " + cf1); }; if (async) executor.execute(cancel); else cancel.run(); @@ -474,16 +503,20 @@ public class CancelRequestTest implements HttpServerAdapters { .POST(HttpRequest.BodyPublishers.ofByteArrays(iterable)) .setOption(H3_DISCOVERY, config) .build(); + var requestLatch = addLatchFor(req); + BodyHandler handler = BodyHandlers.ofString(); CountDownLatch latch = new CountDownLatch(1); + out.println(now() + "Sending (async): " + req.uri()); CompletableFuture> response = client.sendAsync(req, handler); - var cf1 = response.whenComplete((r,t) -> System.out.println(t)); + var cf1 = response.whenComplete((r,t) -> System.out.println(now() + t)); CompletableFuture> cf2 = cf1.whenComplete((r,t) -> latch.countDown()); + out.println(now() + "iteration: " + i + ", req sent: " + req.uri()); out.println("response: " + response); out.println("cf1: " + cf1); out.println("cf2: " + cf2); cancelFuture.complete(cf1); - out.println("response after cancel: " + response); + out.println(now() + "response after cancel: " + response); out.println("cf1 after cancel: " + cf1); out.println("cf2 after cancel: " + cf2); try { @@ -491,8 +524,10 @@ public class CancelRequestTest implements HttpServerAdapters { assertEquals(body, String.join("", BODY.split("\\|"))); throw new AssertionError("Expected CancellationException not received"); } catch (ExecutionException x) { - out.println("Got expected exception: " + x); + out.println(now() + "Got expected exception: " + x); assertTrue(isCancelled(x)); + } finally { + requestLatch.countDown(); } // Cancelling the request may cause an IOException instead... @@ -500,7 +535,7 @@ public class CancelRequestTest implements HttpServerAdapters { try { cf1.get(); } catch (CancellationException | ExecutionException x) { - out.println("Got expected exception: " + x); + out.println(now() + "Got expected exception: " + x); assertTrue(isCancelled(x)); hasCancellationException = x instanceof CancellationException; } @@ -521,7 +556,7 @@ public class CancelRequestTest implements HttpServerAdapters { Throwable cause = wrapped; if (CancellationException.class.isAssignableFrom(wrapped.getClass())) { cause = wrapped.getCause(); - out.println("CancellationException cause: " + x); + out.println(now() + "CancellationException cause: " + x); } else if (!isCancelled(cause)) { throw new RuntimeException("Unexpected cause: " + cause); } @@ -531,10 +566,10 @@ public class CancelRequestTest implements HttpServerAdapters { throw new RuntimeException("Unexpected timeout exception", cause); } if (mayInterruptIfRunning) { - out.println("Got expected exception: " + wrapped); + out.println(now() + "Got expected exception: " + wrapped); out.println("\tcause: " + cause); } else { - out.println("Unexpected exception: " + wrapped); + out.println(now() + "Unexpected exception: " + wrapped); wrapped.printStackTrace(out); throw x; } @@ -571,7 +606,7 @@ public class CancelRequestTest implements HttpServerAdapters { Thread main = Thread.currentThread(); CompletableFuture interruptingThread = new CompletableFuture<>(); - var uriStr = uri + "/post/req=" + i; + var uriStr = uri + "/post/i=" + i; Runnable interrupt = () -> { Thread current = Thread.currentThread(); out.printf("%s Interrupting main from: %s (%s)%n", now(), current, uriStr); @@ -593,39 +628,42 @@ public class CancelRequestTest implements HttpServerAdapters { .POST(HttpRequest.BodyPublishers.ofByteArrays(iterable)) .setOption(H3_DISCOVERY, config) .build(); + var requestLatch = addLatchFor(req); + String body = null; Exception failed = null; try { - out.println("Sending: " + uriStr); + out.println(now() + "Sending: " + req.uri()); body = client.send(req, BodyHandlers.ofString()).body(); } catch (Exception x) { failed = x; } - out.println(uriStr + ": got result or exception"); + requestLatch.countDown(); + out.println(now() + req.uri() + ": got result or exception"); if (failed instanceof InterruptedException) { - out.println(uriStr + ": Got expected exception: " + failed); + out.println(now() + req.uri() + ": Got expected exception: " + failed); } else if (failed instanceof IOException) { - out.println(uriStr + ": got IOException: " + failed); + out.println(now() + req.uri() + ": got IOException: " + failed); // that could be OK if the main thread was interrupted // from the main thread: the interrupted status could have // been caught by writing to the socket from the main // thread. if (interruptingThread.isDone() && interruptingThread.get() == main) { - out.println(uriStr + ": Accepting IOException: " + failed); + out.println(now() + req.uri() + ": Accepting IOException: " + failed); failed.printStackTrace(out); } else { - out.println(uriStr + ": unexpected exception: " + failed); + out.println(now() + req.uri() + ": unexpected exception: " + failed); throw failed; } } else if (failed != null) { - out.println(uriStr + ": unexpected exception: " + failed); + out.println(now() + req.uri() + ": unexpected exception: " + failed); throw failed; } else { assert failed == null; - out.println(uriStr + ": got body: " + body); + out.println(now() + req.uri() + ": got body: " + body); assertEquals(body, String.join("", BODY.split("\\|"))); } - out.println("next iteration"); + out.println(now() + "next iteration"); var error = TRACKER.check(tracker, 2000, (t) -> t.getOutstandingOperations() > 0 || t.getOutstandingSubscribers() > 0, @@ -700,7 +738,7 @@ public class CancelRequestTest implements HttpServerAdapters { } finally { if (fail != null) { if (sharedClientName != null) { - System.err.println("Shared client name is: " + sharedClientName); + System.err.println(now() + "Shared client name is: " + sharedClientName); } throw fail; } @@ -719,9 +757,9 @@ public class CancelRequestTest implements HttpServerAdapters { @Override public void handle(HttpTestExchange t) throws IOException { try { - out.println("HTTPSlowHandler received request to " + t.getRequestURI()); - System.err.println("HTTPSlowHandler received request to " + t.getRequestURI()); - + out.println(now() + "HTTPSlowHandler received request to " + t.getRequestURI()); + System.err.println(now() + "HTTPSlowHandler received request to " + t.getRequestURI()); + var requestLatch = latches.get(t.getRequestURI().getRawQuery()); boolean isThreadInterrupt = isThreadInterrupt(t); byte[] req; try (InputStream is = t.getRequestBody()) { @@ -729,7 +767,7 @@ public class CancelRequestTest implements HttpServerAdapters { } t.sendResponseHeaders(200, -1); // chunked/variable try (OutputStream os = t.getResponseBody()) { - // lets split the response in several chunks... + // let's split the response in several chunks... String msg = (req != null && req.length != 0) ? new String(req, UTF_8) : BODY; @@ -743,16 +781,29 @@ public class CancelRequestTest implements HttpServerAdapters { } catch (InterruptedException x) { // OK } - out.printf("Server wrote %d bytes%n", req.length); + out.printf(now() + "Server wrote %d bytes%n", req.length); + } + if (requestLatch != null) { + out.printf(now() + "Server awaiting latch %s for %s%n", + requestLatch, t.getRequestURI()); + try { + requestLatch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + out.printf(now() + " ...latch released%n"); } } + } catch (IOException io) { + out.println(now() + "HTTPSlowHandler: IOexception is not unexpected: " + io); + throw io; } catch (Throwable e) { - out.println("HTTPSlowHandler: unexpected exception: " + e); + out.println(now() + "HTTPSlowHandler: unexpected exception: " + e); e.printStackTrace(); throw e; } finally { - out.printf("HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); - System.err.printf("HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); + out.printf(now() + "HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); + System.err.printf(now() + "HTTPSlowHandler reply sent: %s%n", t.getRequestURI()); } } } From c1c543cc81b4b73ebf228fb817227309b0cff990 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Fri, 30 Jan 2026 16:10:11 +0000 Subject: [PATCH 062/215] 8210336: DateTimeFormatter predefined formatters should support short time zone offsets Reviewed-by: jlu, rriggs --- .../java/time/format/DateTimeFormatter.java | 23 +++++++- .../time/tck/java/time/TCKOffsetTime.java | 6 ++- .../time/format/TestDateTimeFormatter.java | 54 ++++++++++++++++++- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java b/src/java.base/share/classes/java/time/format/DateTimeFormatter.java index 16d7193c556..9368cf54afd 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatter.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatter.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 @@ -817,6 +817,7 @@ public final class DateTimeFormatter { *

  • The {@link #ISO_LOCAL_DATE} *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -829,7 +830,9 @@ public final class DateTimeFormatter { ISO_OFFSET_DATE = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_DATE) + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); } @@ -846,6 +849,7 @@ public final class DateTimeFormatter { *

  • If the offset is not available then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -862,7 +866,9 @@ public final class DateTimeFormatter { .parseCaseInsensitive() .append(ISO_LOCAL_DATE) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); } @@ -919,6 +925,7 @@ public final class DateTimeFormatter { *

  • The {@link #ISO_LOCAL_TIME} *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -930,7 +937,9 @@ public final class DateTimeFormatter { ISO_OFFSET_TIME = new DateTimeFormatterBuilder() .parseCaseInsensitive() .append(ISO_LOCAL_TIME) + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, null); } @@ -947,6 +956,7 @@ public final class DateTimeFormatter { *

  • If the offset is not available then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -962,7 +972,9 @@ public final class DateTimeFormatter { .parseCaseInsensitive() .append(ISO_LOCAL_TIME) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, null); } @@ -1075,6 +1087,7 @@ public final class DateTimeFormatter { *

  • If the offset is not available to format or parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. *
  • If the zone ID is not available or is a {@code ZoneOffset} then the format is complete. *
  • An open square bracket '['. *
  • The {@link ZoneId#getId() zone ID}. This is not part of the ISO-8601 standard. @@ -1094,7 +1107,9 @@ public final class DateTimeFormatter { ISO_DATE_TIME = new DateTimeFormatterBuilder() .append(ISO_LOCAL_DATE_TIME) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .optionalStart() .appendLiteral('[') .parseCaseSensitive() @@ -1121,6 +1136,7 @@ public final class DateTimeFormatter { *
  • If the offset is not available to format or parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -1139,7 +1155,9 @@ public final class DateTimeFormatter { .appendLiteral('-') .appendValue(DAY_OF_YEAR, 3) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); } @@ -1165,6 +1183,7 @@ public final class DateTimeFormatter { *

  • If the offset is not available to format or parse then the format is complete. *
  • The {@link ZoneOffset#getId() offset ID}. If the offset has seconds then * they will be handled even though this is not part of the ISO-8601 standard. + * The offset parsing is lenient, which allows the minutes and seconds to be optional. * Parsing is case insensitive. * *

    @@ -1185,7 +1204,9 @@ public final class DateTimeFormatter { .appendLiteral('-') .appendValue(DAY_OF_WEEK, 1) .optionalStart() + .parseLenient() .appendOffsetId() + .parseStrict() .toFormatter(ResolverStyle.STRICT, IsoChronology.INSTANCE); } diff --git a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java b/test/jdk/java/time/tck/java/time/TCKOffsetTime.java index 7ea9504edbc..8aab9f53977 100644 --- a/test/jdk/java/time/tck/java/time/TCKOffsetTime.java +++ b/test/jdk/java/time/tck/java/time/TCKOffsetTime.java @@ -421,7 +421,6 @@ public class TCKOffsetTime extends AbstractDateTimeTest { {"00;00"}, {"12-00"}, {"-01:00"}, - {"00:00:00-09"}, {"00:00:00,09"}, {"00:00:abs"}, {"11"}, @@ -436,6 +435,11 @@ public class TCKOffsetTime extends AbstractDateTimeTest { Assertions.assertThrows(DateTimeParseException.class, () -> OffsetTime.parse(unparsable)); } + @Test + public void factory_parse_hourOnlyOffset() { + Assertions.assertDoesNotThrow(() -> OffsetTime.parse("00:00:00-09")); + } + //-----------------------------------------------------------------------s @Test public void factory_parse_illegalHour() { diff --git a/test/jdk/java/time/test/java/time/format/TestDateTimeFormatter.java b/test/jdk/java/time/test/java/time/format/TestDateTimeFormatter.java index 2ccc0da8f58..d6c73f6bc74 100644 --- a/test/jdk/java/time/test/java/time/format/TestDateTimeFormatter.java +++ b/test/jdk/java/time/test/java/time/format/TestDateTimeFormatter.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 @@ -59,6 +59,7 @@ */ package test.java.time.format; +import static java.time.format.DateTimeFormatter.*; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -93,15 +94,19 @@ import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; import java.util.Locale; import java.util.function.Function; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * Test DateTimeFormatter. - * @bug 8085887 8293146 + * @bug 8085887 8293146 8210336 */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TestDateTimeFormatter { @@ -333,4 +338,49 @@ public class TestDateTimeFormatter { assertThrows(DateTimeException.class, () -> LocalDate.parse(weekDate, f)); } } + + private static Stream data_iso_short_offset_parse() { + return Stream.of( + Arguments.of("20260123-01", BASIC_ISO_DATE, "20260123-0100"), + Arguments.of("20260123+00", BASIC_ISO_DATE, "20260123Z"), + Arguments.of("20260123-00", BASIC_ISO_DATE, "20260123Z"), + Arguments.of("2026-01-23-01", ISO_DATE, "2026-01-23-01:00"), + Arguments.of("2026-01-23+00", ISO_DATE, "2026-01-23Z"), + Arguments.of("2026-01-23-00", ISO_DATE, "2026-01-23Z"), + Arguments.of("2026-01-23T11:30:59-01", ISO_DATE_TIME, "2026-01-23T11:30:59-01:00"), + Arguments.of("2026-01-23T11:30:59+00", ISO_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-00", ISO_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("11:30:59-01", ISO_TIME, "11:30:59-01:00"), + Arguments.of("11:30:59+00", ISO_TIME, "11:30:59Z"), + Arguments.of("11:30:59-00", ISO_TIME, "11:30:59Z"), + Arguments.of("2026-01-23-01", ISO_OFFSET_DATE, "2026-01-23-01:00"), + Arguments.of("2026-01-23+00", ISO_OFFSET_DATE, "2026-01-23Z"), + Arguments.of("2026-01-23-00", ISO_OFFSET_DATE, "2026-01-23Z"), + Arguments.of("2026-01-23T11:30:59-01", ISO_OFFSET_DATE_TIME, "2026-01-23T11:30:59-01:00"), + Arguments.of("2026-01-23T11:30:59+00", ISO_OFFSET_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-00", ISO_OFFSET_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("11:30:59-01", ISO_OFFSET_TIME, "11:30:59-01:00"), + Arguments.of("11:30:59+00", ISO_OFFSET_TIME, "11:30:59Z"), + Arguments.of("11:30:59-00", ISO_OFFSET_TIME, "11:30:59Z"), + Arguments.of("2026-023-01", ISO_ORDINAL_DATE, "2026-023-01:00"), + Arguments.of("2026-023+00", ISO_ORDINAL_DATE, "2026-023Z"), + Arguments.of("2026-023-00", ISO_ORDINAL_DATE, "2026-023Z"), + Arguments.of("2026-W04-5-01", ISO_WEEK_DATE, "2026-W04-5-01:00"), + Arguments.of("2026-W04-5+00", ISO_WEEK_DATE, "2026-W04-5Z"), + Arguments.of("2026-W04-5-00", ISO_WEEK_DATE, "2026-W04-5Z"), + Arguments.of("2026-01-23T11:30:59-01", ISO_ZONED_DATE_TIME, "2026-01-23T11:30:59-01:00"), + Arguments.of("2026-01-23T11:30:59+00", ISO_ZONED_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-00", ISO_ZONED_DATE_TIME, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-01", ISO_INSTANT, "2026-01-23T12:30:59Z"), + Arguments.of("2026-01-23T11:30:59+00", ISO_INSTANT, "2026-01-23T11:30:59Z"), + Arguments.of("2026-01-23T11:30:59-00", ISO_INSTANT, "2026-01-23T11:30:59Z") + ); + } + + // Checks if predefined ISO formatters can parse hour-only offsets + @ParameterizedTest + @MethodSource("data_iso_short_offset_parse") + public void test_iso_short_offset_parse(String text, DateTimeFormatter formatter, String expected) { + assertEquals(expected, formatter.format(formatter.parse(text))); + } } From 673cd6ed0c4ebbb301346e8e251d1674f363c0d8 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Fri, 30 Jan 2026 16:54:47 +0000 Subject: [PATCH 063/215] 8374449: Shenandoah: Leaf locks used by Shenandoah need lower ranks Reviewed-by: ysr --- .../share/gc/shenandoah/shenandoahControlThread.cpp | 2 +- src/hotspot/share/gc/shenandoah/shenandoahController.hpp | 7 +++++-- .../gc/shenandoah/shenandoahGenerationalControlThread.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 590e7831f07..bc11659c5e5 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -44,7 +44,7 @@ ShenandoahControlThread::ShenandoahControlThread() : ShenandoahController(), _requested_gc_cause(GCCause::_no_gc), _degen_point(ShenandoahGC::_degenerated_outside_cycle), - _control_lock(Mutex::nosafepoint - 2, "ShenandoahGCRequest_lock", true) { + _control_lock(CONTROL_LOCK_RANK, "ShenandoahControl_lock", true) { set_name("Shenandoah Control Thread"); create_and_start(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp index a6a699fac3b..b8ff4df4771 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp @@ -42,6 +42,9 @@ private: shenandoah_padding(1); protected: + const Mutex::Rank WAITERS_LOCK_RANK = Mutex::safepoint - 5; + const Mutex::Rank CONTROL_LOCK_RANK = Mutex::nosafepoint - 2; + // While we could have a single lock for these, it may risk unblocking // GC waiters when alloc failure GC cycle finishes. We want instead // to make complete explicit cycle for demanding customers. @@ -54,8 +57,8 @@ protected: public: ShenandoahController(): _gc_id(0), - _alloc_failure_waiters_lock(Mutex::safepoint-2, "ShenandoahAllocFailureGC_lock", true), - _gc_waiters_lock(Mutex::safepoint-2, "ShenandoahRequestedGC_lock", true) + _alloc_failure_waiters_lock(WAITERS_LOCK_RANK, "ShenandoahAllocFailureWaiters_lock", true), + _gc_waiters_lock(WAITERS_LOCK_RANK, "ShenandoahGCWaiters_lock", true) { } // Request a collection cycle. This handles "explicit" gc requests diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp index 018b4898a19..3b57190cc75 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp @@ -47,7 +47,7 @@ #include "utilities/events.hpp" ShenandoahGenerationalControlThread::ShenandoahGenerationalControlThread() : - _control_lock(Mutex::nosafepoint - 2, "ShenandoahGCRequest_lock", true), + _control_lock(CONTROL_LOCK_RANK, "ShenandoahGCRequest_lock", true), _requested_gc_cause(GCCause::_no_gc), _requested_generation(nullptr), _gc_mode(none), From ee60eff1ec9eddcdedc12c1707fbcca0025e71d6 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Fri, 30 Jan 2026 17:41:50 +0000 Subject: [PATCH 064/215] 8376038: Refactor java/sql tests to use JUnit 8376629: Refactor javax/sql tests to use JUnit Reviewed-by: lancea --- test/jdk/java/sql/JavatimeTest.java | 173 ----- test/jdk/java/sql/test/TEST.properties | 3 + .../test/sql/BatchUpdateExceptionTests.java | 10 +- .../test/sql/CallableStatementTests.java | 65 +- .../test/sql/ConnectionTests.java | 59 +- .../test/sql/DataTruncationTests.java | 10 +- .../sql/{testng => }/test/sql/DateTests.java | 124 ++-- .../test/sql/DriverManagerTests.java | 80 +-- test/jdk/java/sql/test/sql/JavatimeTest.java | 184 +++++ .../test/sql/PreparedStatementTests.java | 65 +- .../test/sql/SQLClientInfoExceptionTests.java | 10 +- .../test/sql/SQLDataExceptionTests.java | 10 +- .../test/sql/SQLExceptionTests.java | 10 +- .../SQLFeatureNotSupportedExceptionTests.java | 10 +- ...rityConstraintViolationExceptionTests.java | 10 +- ...nvalidAuthorizationSpecExceptionTests.java | 10 +- ...LNonTransientConnectionExceptionTests.java | 10 +- .../sql/SQLNonTransientExceptionTests.java | 10 +- .../sql/SQLRecoverableExceptionTests.java | 10 +- .../sql/SQLSyntaxErrorExceptionTests.java | 10 +- .../test/sql/SQLTimeoutExceptionTests.java | 10 +- .../SQLTransactionRollbackExceptionTests.java | 10 +- .../SQLTransientConnectionExceptionTests.java | 10 +- .../test/sql/SQLTransientExceptionTests.java | 10 +- .../test/sql/SQLWarningTests.java | 10 +- .../{testng => }/test/sql/StatementTests.java | 63 +- .../sql/{testng => }/test/sql/TimeTests.java | 136 ++-- .../{testng => }/test/sql/TimestampTests.java | 296 ++++---- .../DriverManagerInitTests.java | 8 +- .../DriverManagerModuleTests.java | 34 +- .../test/sql/drivermanager/TEST.properties | 4 + test/jdk/java/sql/testng/TEST.properties | 4 - .../java/sql/{testng => }/util/BaseTest.java | 146 ++-- .../{testng => }/util/DriverActionImpl.java | 2 +- .../util/SerializedBatchUpdateException.java | 2 +- .../util/StubCallableStatement.java | 2 +- .../sql/{testng => }/util/StubConnection.java | 2 +- .../util/StubDatabaseMetaData.java | 2 +- .../sql/{testng => }/util/StubDriver.java | 2 +- .../sql/{testng => }/util/StubDriverDA.java | 2 +- .../util/StubPreparedStatement.java | 2 +- .../sql/{testng => }/util/StubStatement.java | 2 +- .../javax/sql/{testng => }/TEST.properties | 6 +- .../services/javax.sql.rowset.RowSetFactory | 0 .../services/javax.sql.rowset.RowSetFactory | 0 test/jdk/javax/sql/rowset/TEST.properties | 1 - .../serial/SerialBlob/SetBinaryStream.java | 42 -- .../serial/SerialClob/SetAsciiStream.java | 42 -- .../serial/SerialClob/SetCharacterStream.java | 42 -- .../test/rowset/BaseRowSetTests.java | 124 ++-- .../test/rowset/CommonRowSetTests.java | 637 ++++++++++-------- .../test/rowset/RowSetFactoryTests.java | 43 +- .../test/rowset/RowSetMetaDataTests.java | 339 +++++----- .../test/rowset/RowSetProviderTests.java | 69 +- .../test/rowset/RowSetWarningTests.java | 8 +- .../cachedrowset/CachedRowSetTests.java | 2 +- .../cachedrowset/CommonCachedRowSetTests.java | 544 +++++++++------ .../rowset/filteredrowset/CityFilter.java | 2 +- .../filteredrowset/FilteredRowSetTests.java | 36 +- .../filteredrowset/PrimaryKeyFilter.java | 2 +- .../JdbcRowSetDriverManagerTest.java | 8 +- .../rowset/joinrowset/JoinRowSetTests.java | 76 ++- .../rowset/resourcebundle/TEST.properties | 2 + .../resourcebundle}/ValidateGetBundle.java | 5 +- .../ValidateResourceBundleAccess.java | 16 +- .../test/rowset/serial/SQLInputImplTests.java | 34 +- .../rowset/serial/SQLOutputImplTests.java | 38 +- .../test/rowset/serial/SerialArrayTests.java | 110 +-- .../test/rowset/serial/SerialBlobTests.java | 233 ++++--- .../test/rowset/serial/SerialClobTests.java | 251 ++++--- .../rowset/serial/SerialDataLinkTests.java | 18 +- .../rowset/serial/SerialExceptionTests.java | 8 +- .../rowset/serial/SerialJavaObjectTests.java | 22 +- .../test/rowset/serial/SerialRefTests.java | 30 +- .../test/rowset/serial/SerialStructTests.java | 16 +- .../rowset/spi/SyncFactoryExceptionTests.java | 8 +- .../test/rowset/spi/SyncFactoryTests.java | 30 +- .../spi/SyncProviderExceptionTests.java | 19 +- .../webrowset/CommonWebRowSetTests.java | 103 +-- .../test/rowset/webrowset/WebRowSetTests.java | 2 +- .../util/PropertyStubProvider.java | 2 +- .../sql/{testng => }/util/StubArray.java | 2 +- .../sql/{testng => }/util/StubBaseRowSet.java | 2 +- .../javax/sql/{testng => }/util/StubBlob.java | 2 +- .../util/StubCachedRowSetImpl.java | 2 +- .../javax/sql/{testng => }/util/StubClob.java | 2 +- .../sql/{testng => }/util/StubContext.java | 2 +- .../util/StubFilteredRowSetImpl.java | 2 +- .../{testng => }/util/StubJdbcRowSetImpl.java | 2 +- .../{testng => }/util/StubJoinRowSetImpl.java | 2 +- .../sql/{testng => }/util/StubNClob.java | 2 +- .../javax/sql/{testng => }/util/StubRef.java | 2 +- .../sql/{testng => }/util/StubRowId.java | 2 +- .../{testng => }/util/StubRowSetFactory.java | 2 +- .../sql/{testng => }/util/StubSQLXML.java | 2 +- .../sql/{testng => }/util/StubStruct.java | 2 +- .../{testng => }/util/StubSyncProvider.java | 2 +- .../{testng => }/util/StubSyncResolver.java | 2 +- .../{testng => }/util/StubWebRowSetImpl.java | 2 +- .../sql/{testng => }/util/SuperHero.java | 2 +- .../{testng => }/util/TestRowSetListener.java | 2 +- .../{testng => }/util/TestSQLDataImpl.java | 2 +- .../sql/{testng => }/xml/COFFEE_ROWS.xml | 0 .../{testng => }/xml/DELETED_COFFEE_ROWS.xml | 0 .../{testng => }/xml/INSERTED_COFFEE_ROWS.xml | 0 .../xml/MODFIED_DELETED_COFFEE_ROWS.xml | 0 .../{testng => }/xml/UPDATED_COFFEE_ROWS.xml | 0 .../xml/UPDATED_INSERTED_COFFEE_ROWS.xml | 0 108 files changed, 2511 insertions(+), 2133 deletions(-) delete mode 100644 test/jdk/java/sql/JavatimeTest.java create mode 100644 test/jdk/java/sql/test/TEST.properties rename test/jdk/java/sql/{testng => }/test/sql/BatchUpdateExceptionTests.java (98%) rename test/jdk/java/sql/{testng => }/test/sql/CallableStatementTests.java (62%) rename test/jdk/java/sql/{testng => }/test/sql/ConnectionTests.java (63%) rename test/jdk/java/sql/{testng => }/test/sql/DataTruncationTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/DateTests.java (80%) rename test/jdk/java/sql/{testng => }/test/sql/DriverManagerTests.java (84%) create mode 100644 test/jdk/java/sql/test/sql/JavatimeTest.java rename test/jdk/java/sql/{testng => }/test/sql/PreparedStatementTests.java (62%) rename test/jdk/java/sql/{testng => }/test/sql/SQLClientInfoExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLDataExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLFeatureNotSupportedExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLIntegrityConstraintViolationExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLNonTransientConnectionExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLNonTransientExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLRecoverableExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLSyntaxErrorExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLTimeoutExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLTransactionRollbackExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLTransientConnectionExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLTransientExceptionTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/SQLWarningTests.java (97%) rename test/jdk/java/sql/{testng => }/test/sql/StatementTests.java (62%) rename test/jdk/java/sql/{testng => }/test/sql/TimeTests.java (76%) rename test/jdk/java/sql/{testng => }/test/sql/TimestampTests.java (73%) rename test/jdk/java/sql/{testng/test/sql/othervm => test/sql/drivermanager}/DriverManagerInitTests.java (93%) rename test/jdk/java/sql/{driverModuleTests => test/sql/drivermanager}/DriverManagerModuleTests.java (84%) create mode 100644 test/jdk/java/sql/test/sql/drivermanager/TEST.properties delete mode 100644 test/jdk/java/sql/testng/TEST.properties rename test/jdk/java/sql/{testng => }/util/BaseTest.java (54%) rename test/jdk/java/sql/{testng => }/util/DriverActionImpl.java (94%) rename test/jdk/java/sql/{testng => }/util/SerializedBatchUpdateException.java (99%) rename test/jdk/java/sql/{testng => }/util/StubCallableStatement.java (99%) rename test/jdk/java/sql/{testng => }/util/StubConnection.java (99%) rename test/jdk/java/sql/{testng => }/util/StubDatabaseMetaData.java (99%) rename test/jdk/java/sql/{testng => }/util/StubDriver.java (96%) rename test/jdk/java/sql/{testng => }/util/StubDriverDA.java (96%) rename test/jdk/java/sql/{testng => }/util/StubPreparedStatement.java (99%) rename test/jdk/java/sql/{testng => }/util/StubStatement.java (99%) rename test/jdk/javax/sql/{testng => }/TEST.properties (68%) rename test/jdk/javax/sql/{testng => }/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory (100%) rename test/jdk/javax/sql/{testng => }/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory (100%) delete mode 100644 test/jdk/javax/sql/rowset/TEST.properties delete mode 100644 test/jdk/javax/sql/rowset/serial/SerialBlob/SetBinaryStream.java delete mode 100644 test/jdk/javax/sql/rowset/serial/SerialClob/SetAsciiStream.java delete mode 100644 test/jdk/javax/sql/rowset/serial/SerialClob/SetCharacterStream.java rename test/jdk/javax/sql/{testng => }/test/rowset/BaseRowSetTests.java (83%) rename test/jdk/javax/sql/{testng => }/test/rowset/CommonRowSetTests.java (67%) rename test/jdk/javax/sql/{testng => }/test/rowset/RowSetFactoryTests.java (80%) rename test/jdk/javax/sql/{testng => }/test/rowset/RowSetMetaDataTests.java (55%) rename test/jdk/javax/sql/{testng => }/test/rowset/RowSetProviderTests.java (79%) rename test/jdk/javax/sql/{testng => }/test/rowset/RowSetWarningTests.java (97%) rename test/jdk/javax/sql/{testng => }/test/rowset/cachedrowset/CachedRowSetTests.java (94%) rename test/jdk/javax/sql/{testng => }/test/rowset/cachedrowset/CommonCachedRowSetTests.java (79%) rename test/jdk/javax/sql/{testng => }/test/rowset/filteredrowset/CityFilter.java (97%) rename test/jdk/javax/sql/{testng => }/test/rowset/filteredrowset/FilteredRowSetTests.java (81%) rename test/jdk/javax/sql/{testng => }/test/rowset/filteredrowset/PrimaryKeyFilter.java (97%) rename test/jdk/javax/sql/{testng => }/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java (95%) rename test/jdk/javax/sql/{testng => }/test/rowset/joinrowset/JoinRowSetTests.java (83%) create mode 100644 test/jdk/javax/sql/test/rowset/resourcebundle/TEST.properties rename test/jdk/javax/sql/{resourceBundleTests => test/rowset/resourcebundle}/ValidateGetBundle.java (94%) rename test/jdk/javax/sql/{testng/test/rowset => test/rowset/resourcebundle}/ValidateResourceBundleAccess.java (89%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SQLInputImplTests.java (92%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SQLOutputImplTests.java (91%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SerialArrayTests.java (72%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SerialBlobTests.java (69%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SerialClobTests.java (72%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SerialDataLinkTests.java (90%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SerialExceptionTests.java (95%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SerialJavaObjectTests.java (86%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SerialRefTests.java (86%) rename test/jdk/javax/sql/{testng => }/test/rowset/serial/SerialStructTests.java (92%) rename test/jdk/javax/sql/{testng => }/test/rowset/spi/SyncFactoryExceptionTests.java (95%) rename test/jdk/javax/sql/{testng => }/test/rowset/spi/SyncFactoryTests.java (92%) rename test/jdk/javax/sql/{testng => }/test/rowset/spi/SyncProviderExceptionTests.java (96%) rename test/jdk/javax/sql/{testng => }/test/rowset/webrowset/CommonWebRowSetTests.java (81%) rename test/jdk/javax/sql/{testng => }/test/rowset/webrowset/WebRowSetTests.java (94%) rename test/jdk/javax/sql/{testng => }/util/PropertyStubProvider.java (92%) rename test/jdk/javax/sql/{testng => }/util/StubArray.java (97%) rename test/jdk/javax/sql/{testng => }/util/StubBaseRowSet.java (99%) rename test/jdk/javax/sql/{testng => }/util/StubBlob.java (97%) rename test/jdk/javax/sql/{testng => }/util/StubCachedRowSetImpl.java (99%) rename test/jdk/javax/sql/{testng => }/util/StubClob.java (97%) rename test/jdk/javax/sql/{testng => }/util/StubContext.java (98%) rename test/jdk/javax/sql/{testng => }/util/StubFilteredRowSetImpl.java (99%) rename test/jdk/javax/sql/{testng => }/util/StubJdbcRowSetImpl.java (99%) rename test/jdk/javax/sql/{testng => }/util/StubJoinRowSetImpl.java (99%) rename test/jdk/javax/sql/{testng => }/util/StubNClob.java (93%) rename test/jdk/javax/sql/{testng => }/util/StubRef.java (95%) rename test/jdk/javax/sql/{testng => }/util/StubRowId.java (93%) rename test/jdk/javax/sql/{testng => }/util/StubRowSetFactory.java (96%) rename test/jdk/javax/sql/{testng => }/util/StubSQLXML.java (97%) rename test/jdk/javax/sql/{testng => }/util/StubStruct.java (95%) rename test/jdk/javax/sql/{testng => }/util/StubSyncProvider.java (97%) rename test/jdk/javax/sql/{testng => }/util/StubSyncResolver.java (99%) rename test/jdk/javax/sql/{testng => }/util/StubWebRowSetImpl.java (99%) rename test/jdk/javax/sql/{testng => }/util/SuperHero.java (97%) rename test/jdk/javax/sql/{testng => }/util/TestRowSetListener.java (96%) rename test/jdk/javax/sql/{testng => }/util/TestSQLDataImpl.java (98%) rename test/jdk/javax/sql/{testng => }/xml/COFFEE_ROWS.xml (100%) rename test/jdk/javax/sql/{testng => }/xml/DELETED_COFFEE_ROWS.xml (100%) rename test/jdk/javax/sql/{testng => }/xml/INSERTED_COFFEE_ROWS.xml (100%) rename test/jdk/javax/sql/{testng => }/xml/MODFIED_DELETED_COFFEE_ROWS.xml (100%) rename test/jdk/javax/sql/{testng => }/xml/UPDATED_COFFEE_ROWS.xml (100%) rename test/jdk/javax/sql/{testng => }/xml/UPDATED_INSERTED_COFFEE_ROWS.xml (100%) diff --git a/test/jdk/java/sql/JavatimeTest.java b/test/jdk/java/sql/JavatimeTest.java deleted file mode 100644 index 89344d7cdc6..00000000000 --- a/test/jdk/java/sql/JavatimeTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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 8007520 - *@summary Test those bridge methods to/from java.time date/time classes - * @key randomness - */ - -import java.util.Random; -import java.sql.Date; -import java.sql.Time; -import java.sql.Timestamp; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; - -public class JavatimeTest { - - static final int NANOS_PER_SECOND = 1000000000; - - public static void main(String[] args) throws Throwable { - int N = 10000; - long t1970 = new java.util.Date(70, 0, 01).getTime(); - Random r = new Random(); - for (int i = 0; i < N; i++) { - int days = r.nextInt(50) * 365 + r.nextInt(365); - long secs = t1970 + days * 86400 + r.nextInt(86400); - int nanos = r.nextInt(NANOS_PER_SECOND); - int nanos_ms = nanos / 1000000 * 1000000; // millis precision - long millis = secs * 1000 + r.nextInt(1000); - - LocalDateTime ldt = LocalDateTime.ofEpochSecond(secs, nanos, ZoneOffset.UTC); - LocalDateTime ldt_ms = LocalDateTime.ofEpochSecond(secs, nanos_ms, ZoneOffset.UTC); - Instant inst = Instant.ofEpochSecond(secs, nanos); - Instant inst_ms = Instant.ofEpochSecond(secs, nanos_ms); - //System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - - /////////// Timestamp //////////////////////////////// - Timestamp ta = new Timestamp(millis); - ta.setNanos(nanos); - if (!isEqual(ta.toLocalDateTime(), ta)) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(ta.toLocalDateTime(), ta); - throw new RuntimeException("FAILED: j.s.ts -> ldt"); - } - if (!isEqual(ldt, Timestamp.valueOf(ldt))) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(ldt, Timestamp.valueOf(ldt)); - throw new RuntimeException("FAILED: ldt -> j.s.ts"); - } - Instant inst0 = ta.toInstant(); - if (ta.getTime() != inst0.toEpochMilli() || - ta.getNanos() != inst0.getNano() || - !ta.equals(Timestamp.from(inst0))) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - throw new RuntimeException("FAILED: j.s.ts -> instant -> j.s.ts"); - } - inst = Instant.ofEpochSecond(secs, nanos); - Timestamp ta0 = Timestamp.from(inst); - if (ta0.getTime() != inst.toEpochMilli() || - ta0.getNanos() != inst.getNano() || - !inst.equals(ta0.toInstant())) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - throw new RuntimeException("FAILED: instant -> timestamp -> instant"); - } - - ////////// java.sql.Date ///////////////////////////// - // j.s.d/t uses j.u.d.equals() !!!!!!!! - java.sql.Date jsd = new java.sql.Date(millis); - if (!isEqual(jsd.toLocalDate(), jsd)) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(jsd.toLocalDate(), jsd); - throw new RuntimeException("FAILED: j.s.d -> ld"); - } - LocalDate ld = ldt.toLocalDate(); - if (!isEqual(ld, java.sql.Date.valueOf(ld))) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(ld, java.sql.Date.valueOf(ld)); - throw new RuntimeException("FAILED: ld -> j.s.d"); - } - ////////// java.sql.Time ///////////////////////////// - java.sql.Time jst = new java.sql.Time(millis); - if (!isEqual(jst.toLocalTime(), jst)) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(jst.toLocalTime(), jst); - throw new RuntimeException("FAILED: j.s.t -> lt"); - } - // millis precision - LocalTime lt = ldt_ms.toLocalTime(); - if (!isEqual(lt, java.sql.Time.valueOf(lt))) { - System.out.printf("ms: %16d ns: %10d ldt:[%s]%n", millis, nanos, ldt); - print(lt, java.sql.Time.valueOf(lt)); - throw new RuntimeException("FAILED: lt -> j.s.t"); - } - } - System.out.println("Passed!"); - } - - private static boolean isEqual(LocalDateTime ldt, Timestamp ts) { - ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); - return zdt.getYear() == ts.getYear() + 1900 && - zdt.getMonthValue() == ts.getMonth() + 1 && - zdt.getDayOfMonth() == ts.getDate() && - zdt.getHour() == ts.getHours() && - zdt.getMinute() == ts.getMinutes() && - zdt.getSecond() == ts.getSeconds() && - zdt.getNano() == ts.getNanos(); - } - - private static void print(LocalDateTime ldt, Timestamp ts) { - ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); - System.out.printf("ldt:ts %d/%d, %d/%d, %d/%d, %d/%d, %d/%d, %d/%d, nano:[%d/%d]%n", - zdt.getYear(), ts.getYear() + 1900, - zdt.getMonthValue(), ts.getMonth() + 1, - zdt.getDayOfMonth(), ts.getDate(), - zdt.getHour(), ts.getHours(), - zdt.getMinute(), ts.getMinutes(), - zdt.getSecond(), ts.getSeconds(), - zdt.getNano(), ts.getNanos()); - } - - private static boolean isEqual(LocalDate ld, java.sql.Date d) { - return ld.getYear() == d.getYear() + 1900 && - ld.getMonthValue() == d.getMonth() + 1 && - ld.getDayOfMonth() == d.getDate(); - } - - private static void print(LocalDate ld, java.sql.Date d) { - System.out.printf("%d/%d, %d/%d, %d/%d%n", - ld.getYear(), d.getYear() + 1900, - ld.getMonthValue(), d.getMonth() + 1, - ld.getDayOfMonth(), d.getDate()); - } - - private static boolean isEqual(LocalTime lt, java.sql.Time t) { - return lt.getHour() == t.getHours() && - lt.getMinute() == t.getMinutes() && - lt.getSecond() == t.getSeconds(); - } - - private static void print(LocalTime lt, java.sql.Time t) { - System.out.printf("%d/%d, %d/%d, %d/%d%n", - lt.getHour(), t.getHours(), - lt.getMinute(), t.getMinutes(), - lt.getSecond(), t.getSeconds()); - } -} diff --git a/test/jdk/java/sql/test/TEST.properties b/test/jdk/java/sql/test/TEST.properties new file mode 100644 index 00000000000..97c9e7bbe18 --- /dev/null +++ b/test/jdk/java/sql/test/TEST.properties @@ -0,0 +1,3 @@ +# JDBC unit tests uses JUnit +JUnit.dirs= . +lib.dirs = /java/sql/util diff --git a/test/jdk/java/sql/testng/test/sql/BatchUpdateExceptionTests.java b/test/jdk/java/sql/test/sql/BatchUpdateExceptionTests.java similarity index 98% rename from test/jdk/java/sql/testng/test/sql/BatchUpdateExceptionTests.java rename to test/jdk/java/sql/test/sql/BatchUpdateExceptionTests.java index 2b1111b7526..2d010b50e41 100644 --- a/test/jdk/java/sql/testng/test/sql/BatchUpdateExceptionTests.java +++ b/test/jdk/java/sql/test/sql/BatchUpdateExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.io.ByteArrayInputStream; import java.io.File; @@ -28,8 +28,10 @@ import java.io.ObjectInputStream; import java.sql.BatchUpdateException; import java.sql.SQLException; import java.util.Arrays; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.SerializedBatchUpdateException; import util.BaseTest; diff --git a/test/jdk/java/sql/testng/test/sql/CallableStatementTests.java b/test/jdk/java/sql/test/sql/CallableStatementTests.java similarity index 62% rename from test/jdk/java/sql/testng/test/sql/CallableStatementTests.java rename to test/jdk/java/sql/test/sql/CallableStatementTests.java index 7a4fe15ecac..dc5c347ef50 100644 --- a/test/jdk/java/sql/testng/test/sql/CallableStatementTests.java +++ b/test/jdk/java/sql/test/sql/CallableStatementTests.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 @@ -20,28 +20,33 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; import util.StubConnection; import java.sql.CallableStatement; import java.sql.SQLException; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class CallableStatementTests extends BaseTest { private CallableStatement cstmt; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { cstmt = new StubConnection().prepareCall("{call SuperHero_Proc(?)}"); } - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { cstmt.close(); } @@ -50,80 +55,84 @@ public class CallableStatementTests extends BaseTest { * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { - assertEquals(cstmt.enquoteLiteral(s), expected); + assertEquals(expected, cstmt.enquoteLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test01() throws SQLException { - cstmt.enquoteLiteral(null); + assertThrows(NullPointerException.class, () -> cstmt.enquoteLiteral(null)); } /* * Validate that enquoteIdentifier returns the expected value */ - @Test(dataProvider = "validIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { - assertEquals(cstmt.enquoteIdentifier(s, alwaysQuote), expected); + assertEquals(expected, cstmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate that a SQLException is thrown for values that are not valid * for a SQL identifier */ - @Test(dataProvider = "invalidIdentifierValues", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidEnquotedIdentifierValues") public void test03(String s, boolean alwaysQuote) throws SQLException { - cstmt.enquoteIdentifier(s, alwaysQuote); + assertThrows(SQLException.class, () -> cstmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate a NullPointerException is thrown is the string passed to * enquoteIdentiifer is null */ - @Test(dataProvider = "trueFalse", - expectedExceptions = NullPointerException.class) + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test04(boolean alwaysQuote) throws SQLException { - cstmt.enquoteIdentifier(null, alwaysQuote); + assertThrows(NullPointerException.class, () -> cstmt.enquoteIdentifier(null, alwaysQuote)); } /* * Validate that isSimpleIdentifier returns the expected value */ - @Test(dataProvider = "simpleIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("simpleIdentifierValues") public void test05(String s, boolean expected) throws SQLException { - assertEquals(cstmt.isSimpleIdentifier(s), expected); + assertEquals(expected, cstmt.isSimpleIdentifier(s)); } /* * Validate a NullPointerException is thrown if the string passed to * isSimpleIdentifier is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test06() throws SQLException { - cstmt.isSimpleIdentifier(null); + assertThrows(NullPointerException.class, () -> cstmt.isSimpleIdentifier(null)); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedNCharLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedNCharLiteralValues") public void test07(String s, String expected) throws SQLException { - assertEquals(cstmt.enquoteNCharLiteral(s), expected); + assertEquals(expected, cstmt.enquoteNCharLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteNCharLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test08() throws SQLException { - cstmt.enquoteNCharLiteral(null); + assertThrows(NullPointerException.class, () -> cstmt.enquoteNCharLiteral(null)); } } diff --git a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java b/test/jdk/java/sql/test/sql/ConnectionTests.java similarity index 63% rename from test/jdk/java/sql/testng/test/sql/ConnectionTests.java rename to test/jdk/java/sql/test/sql/ConnectionTests.java index f40c2784e4a..534b5d7d96a 100644 --- a/test/jdk/java/sql/testng/test/sql/ConnectionTests.java +++ b/test/jdk/java/sql/test/sql/ConnectionTests.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 @@ -20,22 +20,25 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; import util.StubConnection; import java.sql.SQLException; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class ConnectionTests extends BaseTest { protected StubConnection conn; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { conn = new StubConnection(); } @@ -44,80 +47,84 @@ public class ConnectionTests extends BaseTest { * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { - assertEquals(conn.enquoteLiteral(s), expected); + assertEquals(expected, conn.enquoteLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test01() throws SQLException { - conn.enquoteLiteral(null); + assertThrows(NullPointerException.class, () -> conn.enquoteLiteral(null)); } /* * Validate that enquoteIdentifier returns the expected value */ - @Test(dataProvider = "validIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { - assertEquals(conn.enquoteIdentifier(s, alwaysQuote), expected); + assertEquals(expected, conn.enquoteIdentifier(s, alwaysQuote)); } /* * Validate that a SQLException is thrown for values that are not valid * for a SQL identifier */ - @Test(dataProvider = "invalidIdentifierValues", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidEnquotedIdentifierValues") public void test03(String s, boolean alwaysQuote) throws SQLException { - conn.enquoteIdentifier(s, alwaysQuote); + assertThrows(SQLException.class, () -> conn.enquoteIdentifier(s, alwaysQuote)); } /* * Validate a NullPointerException is thrown is the string passed to * enquoteIdentiifer is null */ - @Test(dataProvider = "trueFalse", - expectedExceptions = NullPointerException.class) + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test04(boolean alwaysQuote) throws SQLException { - conn.enquoteIdentifier(null, alwaysQuote); + assertThrows(NullPointerException.class, () -> conn.enquoteIdentifier(null, alwaysQuote)); } /* * Validate that isSimpleIdentifier returns the expected value */ - @Test(dataProvider = "simpleIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("simpleIdentifierValues") public void test05(String s, boolean expected) throws SQLException { - assertEquals(conn.isSimpleIdentifier(s), expected); + assertEquals(expected, conn.isSimpleIdentifier(s)); } /* * Validate a NullPointerException is thrown if the string passed to * isSimpleIdentifier is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test06() throws SQLException { - conn.isSimpleIdentifier(null); + assertThrows(NullPointerException.class, () -> conn.isSimpleIdentifier(null)); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedNCharLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedNCharLiteralValues") public void test07(String s, String expected) throws SQLException { - assertEquals(conn.enquoteNCharLiteral(s), expected); + assertEquals(expected, conn.enquoteNCharLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteNCharLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test08() throws SQLException { - conn.enquoteNCharLiteral(null); + assertThrows(NullPointerException.class, () -> conn.enquoteNCharLiteral(null)); } } diff --git a/test/jdk/java/sql/testng/test/sql/DataTruncationTests.java b/test/jdk/java/sql/test/sql/DataTruncationTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/DataTruncationTests.java rename to test/jdk/java/sql/test/sql/DataTruncationTests.java index fbf7eebe90a..9d221b4a037 100644 --- a/test/jdk/java/sql/testng/test/sql/DataTruncationTests.java +++ b/test/jdk/java/sql/test/sql/DataTruncationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.DataTruncation; import java.sql.SQLException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class DataTruncationTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/DateTests.java b/test/jdk/java/sql/test/sql/DateTests.java similarity index 80% rename from test/jdk/java/sql/testng/test/sql/DateTests.java rename to test/jdk/java/sql/test/sql/DateTests.java index ae6c276c4f6..0c66dd15c98 100644 --- a/test/jdk/java/sql/testng/test/sql/DateTests.java +++ b/test/jdk/java/sql/test/sql/DateTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,14 +20,18 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.Date; -import java.time.Instant; import java.time.LocalDate; -import static org.testng.Assert.*; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; public class DateTests extends BaseTest { @@ -35,17 +39,18 @@ public class DateTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for an invalid Date string */ - @Test(dataProvider = "invalidDateValues", - expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidDateValues") public void test(String d) throws Exception { - Date.valueOf(d); + assertThrows(IllegalArgumentException.class, () -> Date.valueOf(d)); } /* * Test that a date created from a date string is equal to the value * returned from toString() */ - @Test(dataProvider = "validDateValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validDateValues") public void test00(String d, String expectedD) { Date d1 = Date.valueOf(d); Date d2 = Date.valueOf(expectedD); @@ -207,20 +212,20 @@ public class DateTests extends BaseTest { /* * Validate an NPE occurs when a null LocalDate is passed to valueOf */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test15() throws Exception { LocalDate ld = null; - Date.valueOf(ld); + assertThrows(NullPointerException.class, () -> Date.valueOf(ld)); } /* * Validate an UnsupportedOperationException occurs when toInstant() is * called */ - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test public void test16() throws Exception { Date d = Date.valueOf("1961-08-30"); - Instant instant = d.toInstant(); + assertThrows(UnsupportedOperationException.class, d::toInstant); } /* @@ -271,55 +276,55 @@ public class DateTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for calling getHours */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test21() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.getHours(); + assertThrows(IllegalArgumentException.class, d::getHours); } /* * Validate an IllegalArgumentException is thrown for calling getMinutes */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test22() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.getMinutes(); + assertThrows(IllegalArgumentException.class, d::getMinutes); } /* * Validate an IllegalArgumentException is thrown for calling getSeconds */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test23() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.getSeconds(); + assertThrows(IllegalArgumentException.class, d::getSeconds); } /* * Validate an IllegalArgumentException is thrown for calling setHours */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test24() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.setHours(8); + assertThrows(IllegalArgumentException.class, () -> d.setHours(8)); } /* * Validate an IllegalArgumentException is thrown for calling setMinutes */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test25() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.setMinutes(0); + assertThrows(IllegalArgumentException.class, () -> d.setMinutes(0)); } /* * Validate an IllegalArgumentException is thrown for calling setSeconds */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test26() throws Exception { Date d = Date.valueOf("1961-08-30"); - d.setSeconds(0); + assertThrows(IllegalArgumentException.class, () -> d.setSeconds(0)); } /* @@ -327,32 +332,31 @@ public class DateTests extends BaseTest { * to validate that an IllegalArgumentException will be thrown from the * valueOf method */ - @DataProvider(name = "invalidDateValues") - private Object[][] invalidDateValues() { - return new Object[][]{ - {"20009-11-01"}, - {"09-11-01"}, - {"-11-01"}, - {"2009-111-01"}, - {"2009--01"}, - {"2009-13-01"}, - {"2009-11-011"}, - {"2009-11-"}, - {"2009-11-00"}, - {"2009-11-33"}, - {"--"}, - {""}, - {null}, - {"-"}, - {"2009"}, - {"2009-01"}, - {"---"}, - {"2009-13--1"}, - {"1900-1-0"}, - {"2009-01-01 10:50:01"}, - {"1996-12-10 12:26:19.1"}, - {"10:50:01"} - }; + private Stream invalidDateValues() { + return Stream.of( + "20009-11-01", + "09-11-01", + "-11-01", + "2009-111-01", + "2009--01", + "2009-13-01", + "2009-11-011", + "2009-11-", + "2009-11-00", + "2009-11-33", + "--", + "", + null, + "-", + "2009", + "2009-01", + "---", + "2009-13--1", + "1900-1-0", + "2009-01-01 10:50:01", + "1996-12-10 12:26:19.1", + "10:50:01" + ); } /* @@ -360,14 +364,12 @@ public class DateTests extends BaseTest { * to validate that an IllegalArgumentException will not be thrown from the * valueOf method and the corect value from toString() is returned */ - @DataProvider(name = "validDateValues") - private Object[][] validDateValues() { - return new Object[][]{ - {"2009-08-30", "2009-08-30"}, - {"2009-01-8", "2009-01-08"}, - {"2009-1-01", "2009-01-01"}, - {"2009-1-1", "2009-01-01"} - - }; + private Stream validDateValues() { + return Stream.of( + Arguments.of("2009-08-30", "2009-08-30"), + Arguments.of("2009-01-8", "2009-01-08"), + Arguments.of("2009-1-01", "2009-01-01"), + Arguments.of("2009-1-1", "2009-01-01") + ); } } diff --git a/test/jdk/java/sql/testng/test/sql/DriverManagerTests.java b/test/jdk/java/sql/test/sql/DriverManagerTests.java similarity index 84% rename from test/jdk/java/sql/testng/test/sql/DriverManagerTests.java rename to test/jdk/java/sql/test/sql/DriverManagerTests.java index 6b6e8386835..3797b625d21 100644 --- a/test/jdk/java/sql/testng/test/sql/DriverManagerTests.java +++ b/test/jdk/java/sql/test/sql/DriverManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -39,12 +39,10 @@ import java.util.Collections; import java.util.Properties; import java.util.stream.Collectors; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.StubDriver; public class DriverManagerTests { @@ -58,23 +56,11 @@ public class DriverManagerTests { public DriverManagerTests() { } - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { removeAllDrivers(); } - @AfterMethod - public void tearDownMethod() throws Exception { - } - /** * Utility method to remove all registered drivers */ @@ -113,7 +99,7 @@ public class DriverManagerTests { int[] vals = {-1, 0, 5}; for (int val : vals) { DriverManager.setLoginTimeout(val); - assertEquals(val, DriverManager.getLoginTimeout()); + assertEquals(DriverManager.getLoginTimeout(), val); } } @@ -121,20 +107,22 @@ public class DriverManagerTests { * Validate that NullPointerException is thrown when null is passed to * registerDriver */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test1() throws Exception { Driver d = null; - DriverManager.registerDriver(d); + assertThrows(NullPointerException.class, + () -> DriverManager.registerDriver(d)); } /** * Validate that NullPointerException is thrown when null is passed to * registerDriver */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test2() throws Exception { Driver d = null; - DriverManager.registerDriver(d, null); + assertThrows(NullPointerException.class, () -> + DriverManager.registerDriver(d, null)); } /** @@ -150,68 +138,68 @@ public class DriverManagerTests { * Validate that SQLException is thrown when there is no Driver to service * the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test4() throws Exception { - DriverManager.getConnection(InvalidURL); + assertThrows(SQLException.class, () -> DriverManager.getConnection(InvalidURL)); } /** * Validate that SQLException is thrown when there is no Driver to service * the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test5() throws Exception { - DriverManager.getConnection(InvalidURL, new Properties()); + assertThrows(SQLException.class, () -> DriverManager.getConnection(InvalidURL, new Properties())); } /** * Validate that SQLException is thrown when there is no Driver to service * the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test6() throws Exception { - DriverManager.getConnection(InvalidURL, "LuckyDog", "tennisanyone"); + assertThrows(SQLException.class, () -> DriverManager.getConnection(InvalidURL, "LuckyDog", "tennisanyone")); } /** * Validate that SQLException is thrown when null is passed for the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test7() throws Exception { - DriverManager.getConnection(null); + assertThrows(SQLException.class, () -> DriverManager.getConnection(null)); } /** * Validate that SQLException is thrown when null is passed for the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test8() throws Exception { - DriverManager.getConnection(null, new Properties()); + assertThrows(SQLException.class, () -> DriverManager.getConnection(null, new Properties())); } /** * Validate that SQLException is thrown when null is passed for the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test9() throws Exception { - DriverManager.getConnection(null, "LuckyDog", "tennisanyone"); + assertThrows(SQLException.class, () -> DriverManager.getConnection(null, "LuckyDog", "tennisanyone")); } /** * Validate that SQLException is thrown when there is no Driver to service * the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test10() throws Exception { - DriverManager.getDriver(InvalidURL); + assertThrows(SQLException.class, () -> DriverManager.getDriver(InvalidURL)); } /** * Validate that SQLException is thrown when null is passed for the URL */ - @Test(expectedExceptions = SQLException.class) + @Test public void test11() throws Exception { - DriverManager.getDriver(null); + assertThrows(SQLException.class, () -> DriverManager.getDriver(null)); } /** @@ -229,10 +217,10 @@ public class DriverManagerTests { * Validate that SQLException is thrown when the URL is not valid for any of * the registered drivers */ - @Test(expectedExceptions = SQLException.class) + @Test public void test13() throws Exception { DriverManager.registerDriver(new StubDriver()); - DriverManager.getDriver(InvalidURL); + assertThrows(SQLException.class, () -> DriverManager.getDriver(InvalidURL)); } /** @@ -370,9 +358,9 @@ public class DriverManagerTests { } Collection expectedDrivers = Collections.list(DriverManager.getDrivers()); - assertEquals(expectedDrivers.size(), n); + assertEquals(n, expectedDrivers.size()); Collection drivers = DriverManager.drivers().collect(Collectors.toList()); - assertEquals(drivers, expectedDrivers); + assertEquals(expectedDrivers, drivers); } } diff --git a/test/jdk/java/sql/test/sql/JavatimeTest.java b/test/jdk/java/sql/test/sql/JavatimeTest.java new file mode 100644 index 00000000000..acc0ac3e841 --- /dev/null +++ b/test/jdk/java/sql/test/sql/JavatimeTest.java @@ -0,0 +1,184 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 sql; + +/* + * @test + * @bug 8007520 + * @summary Test those bridge methods to/from java.time date/time classes + * @key randomness + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.FieldSource; + +import java.util.List; +import java.util.Random; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class JavatimeTest { + + private static final int NANOS_PER_SECOND = 1000000000; + private static final long t1970 = new java.util.Date(70, 0, 01).getTime(); + private static final Random R = new Random(); + // Data provider contains 10,000 randomized arguments + // which are used as the dates and times for the tests + private static final List DATE_TIME_ARGS = IntStream.range(0, 10_000) + .mapToObj(i -> { + int days = R.nextInt(50) * 365 + R.nextInt(365); + long secs = t1970 + days * 86400 + R.nextInt(86400); + int nanos = R.nextInt(NANOS_PER_SECOND); + int nanos_ms = nanos / 1000000 * 1000000; // millis precision + long millis = secs * 1000 + R.nextInt(1000); + LocalDateTime ldt = LocalDateTime.ofEpochSecond(secs, nanos, ZoneOffset.UTC); + return Arguments.of(millis, nanos, ldt, secs, nanos_ms); + }).toList(); + + @ParameterizedTest(autoCloseArguments = false) + @FieldSource("DATE_TIME_ARGS") + void timestampTest(long millis, int nanos, LocalDateTime ldt, long secs) { + Timestamp ta = new Timestamp(millis); + ta.setNanos(nanos); + assertTrue(isEqual(ta.toLocalDateTime(), ta), + errMsg("j.s.ts -> ldt", millis, nanos, ldt, results(ta.toLocalDateTime(), ta))); + + assertTrue(isEqual(ldt, Timestamp.valueOf(ldt)), + errMsg("ldt -> j.s.ts", millis, nanos, ldt, results(ldt, Timestamp.valueOf(ldt)))); + + Instant inst0 = ta.toInstant(); + assertAll(errMsg("j.s.ts -> instant -> j.s.ts", millis, nanos, ldt), + () -> assertEquals(ta.getTime(), inst0.toEpochMilli()), + () -> assertEquals(ta.getNanos(), inst0.getNano()), + () -> assertEquals(ta, Timestamp.from(inst0)) + ); + + Instant inst = Instant.ofEpochSecond(secs, nanos); + Timestamp ta0 = Timestamp.from(inst); + assertAll(errMsg("instant -> timestamp -> instant", millis, nanos, ldt), + () -> assertEquals(ta0.getTime(), inst.toEpochMilli()), + () -> assertEquals(ta0.getNanos(), inst.getNano()), + () -> assertEquals(inst, ta0.toInstant()) + ); + } + + @ParameterizedTest(autoCloseArguments = false) + @FieldSource("DATE_TIME_ARGS") + void sqlDateTest(long millis, int nanos, LocalDateTime ldt) { + // j.s.d/t uses j.u.d.equals() !!!!!!!! + java.sql.Date jsd = new java.sql.Date(millis); + assertTrue(isEqual(jsd.toLocalDate(), jsd), + errMsg("j.s.d -> ld", millis, nanos, ldt, results(jsd.toLocalDate(), jsd))); + + LocalDate ld = ldt.toLocalDate(); + assertTrue(isEqual(ld, java.sql.Date.valueOf(ld)), + errMsg("ld -> j.s.d", millis, nanos, ldt, results(ld, java.sql.Date.valueOf(ld)))); + } + + @ParameterizedTest(autoCloseArguments = false) + @FieldSource("DATE_TIME_ARGS") + void sqlTimeTest(long millis, int nanos, LocalDateTime ldt, long secs, int nanos_ms) { + java.sql.Time jst = new java.sql.Time(millis); + assertTrue(isEqual(jst.toLocalTime(), jst), + errMsg("j.s.t -> lt", millis, nanos, ldt, results(jst.toLocalTime(), jst))); + + // millis precision + LocalDateTime ldt_ms = LocalDateTime.ofEpochSecond(secs, nanos_ms, ZoneOffset.UTC); + LocalTime lt = ldt_ms.toLocalTime(); + assertTrue(isEqual(lt, java.sql.Time.valueOf(lt)), + errMsg("lt -> j.s.t", millis, nanos, ldt, results(lt, java.sql.Time.valueOf(lt)))); + } + + private static boolean isEqual(LocalDateTime ldt, Timestamp ts) { + ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); + return zdt.getYear() == ts.getYear() + 1900 && + zdt.getMonthValue() == ts.getMonth() + 1 && + zdt.getDayOfMonth() == ts.getDate() && + zdt.getHour() == ts.getHours() && + zdt.getMinute() == ts.getMinutes() && + zdt.getSecond() == ts.getSeconds() && + zdt.getNano() == ts.getNanos(); + } + + private static String results(LocalDateTime ldt, Timestamp ts) { + ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault()); + return "ldt:ts %d/%d, %d/%d, %d/%d, %d/%d, %d/%d, %d/%d, nano:[%d/%d]%n".formatted( + zdt.getYear(), ts.getYear() + 1900, + zdt.getMonthValue(), ts.getMonth() + 1, + zdt.getDayOfMonth(), ts.getDate(), + zdt.getHour(), ts.getHours(), + zdt.getMinute(), ts.getMinutes(), + zdt.getSecond(), ts.getSeconds(), + zdt.getNano(), ts.getNanos()); + } + + private static boolean isEqual(LocalDate ld, java.sql.Date d) { + return ld.getYear() == d.getYear() + 1900 && + ld.getMonthValue() == d.getMonth() + 1 && + ld.getDayOfMonth() == d.getDate(); + } + + private static String results(LocalDate ld, java.sql.Date d) { + return "%d/%d, %d/%d, %d/%d%n".formatted( + ld.getYear(), d.getYear() + 1900, + ld.getMonthValue(), d.getMonth() + 1, + ld.getDayOfMonth(), d.getDate()); + } + + private static boolean isEqual(LocalTime lt, java.sql.Time t) { + return lt.getHour() == t.getHours() && + lt.getMinute() == t.getMinutes() && + lt.getSecond() == t.getSeconds(); + } + + private static String results(LocalTime lt, java.sql.Time t) { + return "%d/%d, %d/%d, %d/%d%n".formatted( + lt.getHour(), t.getHours(), + lt.getMinute(), t.getMinutes(), + lt.getSecond(), t.getSeconds()); + } + + private static String errMsg(String testCase, long millis, int nanos, + LocalDateTime ldt, String results) { + return "FAILED: %s%n INPUTS: ms: %16d ns: %10d ldt:[%s]%n ACTUAL: %s" + .formatted(testCase, millis, nanos, ldt, results); + } + + private static String errMsg(String testCase, long millis, int nanos, + LocalDateTime ldt) { + return "FAILED: %s%n INPUTS: ms: %16d ns: %10d ldt:[%s]%n" + .formatted(testCase, millis, nanos, ldt); + } +} \ No newline at end of file diff --git a/test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java b/test/jdk/java/sql/test/sql/PreparedStatementTests.java similarity index 62% rename from test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java rename to test/jdk/java/sql/test/sql/PreparedStatementTests.java index 7813038361d..574b0369b4e 100644 --- a/test/jdk/java/sql/testng/test/sql/PreparedStatementTests.java +++ b/test/jdk/java/sql/test/sql/PreparedStatementTests.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 @@ -20,29 +20,34 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; import util.StubConnection; import java.sql.PreparedStatement; import java.sql.SQLException; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; public class PreparedStatementTests extends BaseTest { private PreparedStatement pstmt; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { pstmt = new StubConnection().prepareStatement("Select * from foo were bar = ?"); } - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { pstmt.close(); } @@ -51,80 +56,84 @@ public class PreparedStatementTests extends BaseTest { * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { - assertEquals(pstmt.enquoteLiteral(s), expected); + assertEquals(expected, pstmt.enquoteLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test01() throws SQLException { - pstmt.enquoteLiteral(null); + assertThrows(NullPointerException.class, () -> pstmt.enquoteLiteral(null)); } /* * Validate that enquoteIdentifier returns the expected value */ - @Test(dataProvider = "validIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { - assertEquals(pstmt.enquoteIdentifier(s, alwaysQuote), expected); + assertEquals(expected, pstmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate that a SQLException is thrown for values that are not valid * for a SQL identifier */ - @Test(dataProvider = "invalidIdentifierValues", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidEnquotedIdentifierValues") public void test03(String s, boolean alwaysQuote) throws SQLException { - pstmt.enquoteIdentifier(s, alwaysQuote); + assertThrows(SQLException.class, () -> pstmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate a NullPointerException is thrown is the string passed to * enquoteIdentiifer is null */ - @Test(dataProvider = "trueFalse", - expectedExceptions = NullPointerException.class) + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test04(boolean alwaysQuote) throws SQLException { - pstmt.enquoteIdentifier(null, alwaysQuote); + assertThrows(NullPointerException.class, () -> pstmt.enquoteIdentifier(null, alwaysQuote)); } /* * Validate that isSimpleIdentifier returns the expected value */ - @Test(dataProvider = "simpleIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("simpleIdentifierValues") public void test05(String s, boolean expected) throws SQLException { - assertEquals(pstmt.isSimpleIdentifier(s), expected); + assertEquals(expected, pstmt.isSimpleIdentifier(s)); } /* * Validate a NullPointerException is thrown if the string passed to * isSimpleIdentifier is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test06() throws SQLException { - pstmt.isSimpleIdentifier(null); + assertThrows(NullPointerException.class, () -> pstmt.isSimpleIdentifier(null)); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedNCharLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedNCharLiteralValues") public void test07(String s, String expected) throws SQLException { - assertEquals(pstmt.enquoteNCharLiteral(s), expected); + assertEquals(expected, pstmt.enquoteNCharLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteNCharLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test08() throws SQLException { - pstmt.enquoteNCharLiteral(null); + assertThrows(NullPointerException.class, () -> pstmt.enquoteNCharLiteral(null)); } } diff --git a/test/jdk/java/sql/testng/test/sql/SQLClientInfoExceptionTests.java b/test/jdk/java/sql/test/sql/SQLClientInfoExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLClientInfoExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLClientInfoExceptionTests.java index 84230b86758..bea35d95c1c 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLClientInfoExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLClientInfoExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,14 +20,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.ClientInfoStatus; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.util.HashMap; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLClientInfoExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLDataExceptionTests.java b/test/jdk/java/sql/test/sql/SQLDataExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLDataExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLDataExceptionTests.java index 8a5f8d1a2c1..a18f6406e66 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLDataExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLDataExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLDataException; import java.sql.SQLException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLDataExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLExceptionTests.java b/test/jdk/java/sql/test/sql/SQLExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLExceptionTests.java index e3643ef119c..8a7850d8fd9 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,11 +20,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLFeatureNotSupportedExceptionTests.java b/test/jdk/java/sql/test/sql/SQLFeatureNotSupportedExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLFeatureNotSupportedExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLFeatureNotSupportedExceptionTests.java index 5b95894412c..24049addfa1 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLFeatureNotSupportedExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLFeatureNotSupportedExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLFeatureNotSupportedExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLIntegrityConstraintViolationExceptionTests.java b/test/jdk/java/sql/test/sql/SQLIntegrityConstraintViolationExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLIntegrityConstraintViolationExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLIntegrityConstraintViolationExceptionTests.java index 082e0af15c8..c8c0958e3f3 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLIntegrityConstraintViolationExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLIntegrityConstraintViolationExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLIntegrityConstraintViolationException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLIntegrityConstraintViolationExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java b/test/jdk/java/sql/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java index 6e4eaa567ee..82a9a345a0e 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLInvalidAuthorizationSpecExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLInvalidAuthorizationSpecException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLInvalidAuthorizationSpecExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLNonTransientConnectionExceptionTests.java b/test/jdk/java/sql/test/sql/SQLNonTransientConnectionExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLNonTransientConnectionExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLNonTransientConnectionExceptionTests.java index dbd8b685844..fa4c75ed672 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLNonTransientConnectionExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLNonTransientConnectionExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLNonTransientConnectionException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLNonTransientConnectionExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLNonTransientExceptionTests.java b/test/jdk/java/sql/test/sql/SQLNonTransientExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLNonTransientExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLNonTransientExceptionTests.java index 061ffe20840..0f37f905fa7 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLNonTransientExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLNonTransientExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLNonTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLNonTransientExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLRecoverableExceptionTests.java b/test/jdk/java/sql/test/sql/SQLRecoverableExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLRecoverableExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLRecoverableExceptionTests.java index d23a131dbcd..d447e08593c 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLRecoverableExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLRecoverableExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLRecoverableException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLRecoverableExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLSyntaxErrorExceptionTests.java b/test/jdk/java/sql/test/sql/SQLSyntaxErrorExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLSyntaxErrorExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLSyntaxErrorExceptionTests.java index 863213cfcb8..c859f5aa0d2 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLSyntaxErrorExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLSyntaxErrorExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLNonTransientException; import java.sql.SQLSyntaxErrorException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLSyntaxErrorExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLTimeoutExceptionTests.java b/test/jdk/java/sql/test/sql/SQLTimeoutExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLTimeoutExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLTimeoutExceptionTests.java index dfe341545e4..a4e249658d9 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLTimeoutExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLTimeoutExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLTimeoutException; import java.sql.SQLTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLTimeoutExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLTransactionRollbackExceptionTests.java b/test/jdk/java/sql/test/sql/SQLTransactionRollbackExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLTransactionRollbackExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLTransactionRollbackExceptionTests.java index 8453ebe1364..7a7d94cd060 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLTransactionRollbackExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLTransactionRollbackExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLTransactionRollbackException; import java.sql.SQLTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLTransactionRollbackExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLTransientConnectionExceptionTests.java b/test/jdk/java/sql/test/sql/SQLTransientConnectionExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLTransientConnectionExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLTransientConnectionExceptionTests.java index 7999253b1fa..8ac6ef98222 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLTransientConnectionExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLTransientConnectionExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,15 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLTransientConnectionException; import java.sql.SQLTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLTransientConnectionExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLTransientExceptionTests.java b/test/jdk/java/sql/test/sql/SQLTransientExceptionTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLTransientExceptionTests.java rename to test/jdk/java/sql/test/sql/SQLTransientExceptionTests.java index d86f86b8fc6..4c43b6c693e 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLTransientExceptionTests.java +++ b/test/jdk/java/sql/test/sql/SQLTransientExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLTransientException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLTransientExceptionTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/SQLWarningTests.java b/test/jdk/java/sql/test/sql/SQLWarningTests.java similarity index 97% rename from test/jdk/java/sql/testng/test/sql/SQLWarningTests.java rename to test/jdk/java/sql/test/sql/SQLWarningTests.java index 2856742dc5c..ab15a877dbd 100644 --- a/test/jdk/java/sql/testng/test/sql/SQLWarningTests.java +++ b/test/jdk/java/sql/test/sql/SQLWarningTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,12 +20,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.SQLWarning; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SQLWarningTests extends BaseTest { diff --git a/test/jdk/java/sql/testng/test/sql/StatementTests.java b/test/jdk/java/sql/test/sql/StatementTests.java similarity index 62% rename from test/jdk/java/sql/testng/test/sql/StatementTests.java rename to test/jdk/java/sql/test/sql/StatementTests.java index f2bfe712eb5..42e971cc971 100644 --- a/test/jdk/java/sql/testng/test/sql/StatementTests.java +++ b/test/jdk/java/sql/test/sql/StatementTests.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 @@ -20,14 +20,21 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.SQLException; import java.sql.Statement; -import static org.testng.Assert.assertEquals; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -import org.testng.annotations.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; import util.StubConnection; @@ -35,12 +42,12 @@ public class StatementTests extends BaseTest { private Statement stmt; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { stmt = new StubConnection().createStatement(); } - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { stmt.close(); } @@ -48,80 +55,84 @@ public class StatementTests extends BaseTest { * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedLiteralValues") public void test00(String s, String expected) throws SQLException { - assertEquals(stmt.enquoteLiteral(s), expected); + assertEquals(expected, stmt.enquoteLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test01() throws SQLException { - stmt.enquoteLiteral(null); + assertThrows(NullPointerException.class, () -> stmt.enquoteLiteral(null)); } /* * Validate that enquoteIdentifier returns the expected value */ - @Test(dataProvider = "validIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedIdentifierValues") public void test02(String s, boolean alwaysQuote, String expected) throws SQLException { - assertEquals(stmt.enquoteIdentifier(s, alwaysQuote), expected); + assertEquals(expected, stmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate that a SQLException is thrown for values that are not valid * for a SQL identifier */ - @Test(dataProvider = "invalidIdentifierValues", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidEnquotedIdentifierValues") public void test03(String s, boolean alwaysQuote) throws SQLException { - stmt.enquoteIdentifier(s, alwaysQuote); + assertThrows(SQLException.class, () -> stmt.enquoteIdentifier(s, alwaysQuote)); } /* * Validate a NullPointerException is thrown is the string passed to * enquoteIdentiifer is null */ - @Test(dataProvider = "trueFalse", - expectedExceptions = NullPointerException.class) + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test04(boolean alwaysQuote) throws SQLException { - stmt.enquoteIdentifier(null, alwaysQuote); + assertThrows(NullPointerException.class, () -> stmt.enquoteIdentifier(null, alwaysQuote)); } /* * Validate that isSimpleIdentifier returns the expected value */ - @Test(dataProvider = "simpleIdentifierValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("simpleIdentifierValues") public void test05(String s, boolean expected) throws SQLException { - assertEquals(stmt.isSimpleIdentifier(s), expected); + assertEquals(expected, stmt.isSimpleIdentifier(s)); } /* * Validate a NullPointerException is thrown if the string passed to * isSimpleIdentifier is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test06() throws SQLException { - stmt.isSimpleIdentifier(null); + assertThrows(NullPointerException.class, () -> stmt.isSimpleIdentifier(null)); } /* * Verify that enquoteLiteral creates a valid literal and converts every * single quote to two single quotes */ - @Test(dataProvider = "validEnquotedNCharLiteralValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validEnquotedNCharLiteralValues") public void test07(String s, String expected) throws SQLException { - assertEquals(stmt.enquoteNCharLiteral(s), expected); + assertEquals(expected, stmt.enquoteNCharLiteral(s)); } /* * Validate a NullPointerException is thrown if the string passed to * enquoteNCharLiteral is null */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test08() throws SQLException { - stmt.enquoteNCharLiteral(null); + assertThrows(NullPointerException.class, () -> stmt.enquoteNCharLiteral(null)); } } diff --git a/test/jdk/java/sql/testng/test/sql/TimeTests.java b/test/jdk/java/sql/test/sql/TimeTests.java similarity index 76% rename from test/jdk/java/sql/testng/test/sql/TimeTests.java rename to test/jdk/java/sql/test/sql/TimeTests.java index 7b99679754b..71208f03222 100644 --- a/test/jdk/java/sql/testng/test/sql/TimeTests.java +++ b/test/jdk/java/sql/test/sql/TimeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -20,13 +20,18 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.Time; import java.time.LocalTime; -import static org.testng.Assert.*; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; public class TimeTests extends BaseTest { @@ -34,73 +39,73 @@ public class TimeTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for calling getYear */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test01() { Time t = Time.valueOf("08:30:59"); - t.getYear(); + assertThrows(IllegalArgumentException.class, t::getYear); } /* * Validate an IllegalArgumentException is thrown for calling getMonth */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test02() { Time t = Time.valueOf("08:30:59"); - t.getMonth(); + assertThrows(IllegalArgumentException.class, t::getMonth); } /* * Validate an IllegalArgumentException is thrown for calling getDay */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test03() { Time t = Time.valueOf("08:30:59"); - t.getDay(); + assertThrows(IllegalArgumentException.class, t::getDay); } /** * Validate an IllegalArgumentException is thrown for calling getDate */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test04() { Time t = Time.valueOf("08:30:59"); - t.getDate(); + assertThrows(IllegalArgumentException.class, t::getDate); } /* * Validate an IllegalArgumentException is thrown for calling setYear */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test05() { Time t = Time.valueOf("08:30:59"); - t.setYear(8); + assertThrows(IllegalArgumentException.class, () -> t.setYear(8)); } /* * Validate an IllegalArgumentException is thrown for calling setMonth */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test06() { Time t = Time.valueOf("08:30:59"); - t.setMonth(8); + assertThrows(IllegalArgumentException.class, () -> t.setMonth(8)); } /* * Validate an IllegalArgumentException is thrown for calling setDate */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test07() { Time t = Time.valueOf("08:30:59"); - t.setDate(30); + assertThrows(IllegalArgumentException.class, () -> t.setDate(30)); } /* * Validate an IllegalArgumentException is thrown for calling getDate */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test08() { Time t = Time.valueOf("08:30:59"); - t.getDate(); + assertThrows(IllegalArgumentException.class, t::getDate); } /* @@ -128,20 +133,20 @@ public class TimeTests extends BaseTest { /* * Validate an NPE occurs when a null LocalDate is passed to valueOf */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test11() throws Exception { LocalTime ld = null; - Time.valueOf(ld); + assertThrows(NullPointerException.class, () -> Time.valueOf(ld)); } /* * Validate an UnsupportedOperationException occurs when toInstant() is * called */ - @Test(expectedExceptions = UnsupportedOperationException.class) + @Test public void test12() throws Exception { Time t = new Time(System.currentTimeMillis()); - t.toInstant(); + assertThrows(UnsupportedOperationException.class, t::toInstant); } /* @@ -149,7 +154,8 @@ public class TimeTests extends BaseTest { * toString() of the other and that the correct value is returned from * toString() */ - @Test(dataProvider = "validTimeValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validTimeValues") public void test13(String time, String expected) { Time t1 = Time.valueOf(time); Time t2 = Time.valueOf(t1.toString()); @@ -182,10 +188,10 @@ public class TimeTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for an invalid Time string */ - @Test(dataProvider = "invalidTimeValues", - expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidTimeValues") public void test16(String time) throws Exception { - Time.valueOf(time); + assertThrows(IllegalArgumentException.class, () -> Time.valueOf(time)); } /* @@ -299,29 +305,28 @@ public class TimeTests extends BaseTest { * to validate that an IllegalArgumentException will be thrown from the * valueOf method */ - @DataProvider(name = "invalidTimeValues") - private Object[][] invalidTimeValues() { - return new Object[][]{ - {"2009-11-01 10:50:01"}, - {"1961-08-30 10:50:01.1"}, - {"1961-08-30"}, - {"00:00:00."}, - {"10:50:0.1"}, - {":00:00"}, - {"00::00"}, - {"00:00:"}, - {"::"}, - {" : : "}, - {"0a:00:00"}, - {"00:bb:00"}, - {"00:01:cc"}, - {"08:10:Batman"}, - {"08:10:10:10"}, - {"08:10"}, - {"a:b:c"}, - {null}, - {"8:"} - }; + private Stream invalidTimeValues() { + return Stream.of( + "2009-11-01 10:50:01", + "1961-08-30 10:50:01.1", + "1961-08-30", + "00:00:00.", + "10:50:0.1", + ":00:00", + "00::00", + "00:00:", + "::", + " : : ", + "0a:00:00", + "00:bb:00", + "00:01:cc", + "08:10:Batman", + "08:10:10:10", + "08:10", + "a:b:c", + null, + "8:" + ); } /* @@ -330,19 +335,18 @@ public class TimeTests extends BaseTest { * valueOf method. It also contains the expected return value from * toString() */ - @DataProvider(name = "validTimeValues") - private Object[][] validTimeValues() { - return new Object[][]{ - {"10:50:01", "10:50:01"}, - {"01:1:1", "01:01:01"}, - {"01:01:1", "01:01:01"}, - {"1:01:1", "01:01:01"}, - {"2:02:02", "02:02:02"}, - {"2:02:2", "02:02:02"}, - {"10:50:1", "10:50:01"}, - {"00:00:00", "00:00:00"}, - {"08:30:59", "08:30:59"}, - {"9:0:1", "09:00:01"} - }; + private Stream validTimeValues() { + return Stream.of( + Arguments.of("10:50:01", "10:50:01"), + Arguments.of("01:1:1", "01:01:01"), + Arguments.of("01:01:1", "01:01:01"), + Arguments.of("1:01:1", "01:01:01"), + Arguments.of("2:02:02", "02:02:02"), + Arguments.of("2:02:2", "02:02:02"), + Arguments.of("10:50:1", "10:50:01"), + Arguments.of("00:00:00", "00:00:00"), + Arguments.of("08:30:59", "08:30:59"), + Arguments.of("9:0:1", "09:00:01") + ); } } diff --git a/test/jdk/java/sql/testng/test/sql/TimestampTests.java b/test/jdk/java/sql/test/sql/TimestampTests.java similarity index 73% rename from test/jdk/java/sql/testng/test/sql/TimestampTests.java rename to test/jdk/java/sql/test/sql/TimestampTests.java index 6baea9fa26f..33a58c8d857 100644 --- a/test/jdk/java/sql/testng/test/sql/TimestampTests.java +++ b/test/jdk/java/sql/test/sql/TimestampTests.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 @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql; +package sql; import java.sql.Date; import java.sql.Time; @@ -30,11 +30,16 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Calendar; import java.util.TimeZone; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import java.util.stream.Stream; + +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; public class TimestampTests extends BaseTest { @@ -45,7 +50,7 @@ public class TimestampTests extends BaseTest { * Need to set and use a custom TimeZone which does not * observe daylight savings time for this test. */ - @BeforeClass + @BeforeAll public static void setUpClass() throws Exception { defaultTimeZone = TimeZone.getDefault(); TimeZone tzone = TimeZone.getTimeZone("GMT+01"); @@ -56,7 +61,7 @@ public class TimestampTests extends BaseTest { /* * Conservatively reset the default time zone after test. */ - @AfterClass + @AfterAll public static void tearDownClass() throws Exception { TimeZone.setDefault(defaultTimeZone); } @@ -64,10 +69,10 @@ public class TimestampTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for an invalid Timestamp */ - @Test(dataProvider = "invalidTimestampValues", - expectedExceptions = IllegalArgumentException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidTimestampValues") public void test(String ts) throws Exception { - Timestamp.valueOf(ts); + assertThrows(IllegalArgumentException.class, () -> Timestamp.valueOf(ts)); } /* @@ -80,7 +85,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -91,7 +96,7 @@ public class TimestampTests extends BaseTest { String testTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(testTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -104,7 +109,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -117,7 +122,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -130,7 +135,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -142,7 +147,7 @@ public class TimestampTests extends BaseTest { String ExpectedTS = "2005-01-01 10:20:50.00"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(ExpectedTS); - assertEquals(ts, ts2, "Error ts1 != ts2"); + assertEquals(ts2, ts, "Error ts1 != ts2"); } /* @@ -219,7 +224,8 @@ public class TimestampTests extends BaseTest { * Validate that two Timestamps are equal when one is created from the * toString() of the other */ - @Test(dataProvider = "validTimestampValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validTimestampValues") public void test13(String ts, String expectedTS) { Timestamp ts1 = Timestamp.valueOf(ts); Timestamp ts2 = Timestamp.valueOf(ts1.toString()); @@ -263,10 +269,10 @@ public class TimestampTests extends BaseTest { * Validate that a NullPointerException is thrown if a null is passed to * the before method */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test17() throws Exception { Timestamp ts1 = Timestamp.valueOf("1996-12-13 14:15:25.745634"); - ts1.before(null); + assertThrows(NullPointerException.class, () -> ts1.before(null)); } /* @@ -316,10 +322,10 @@ public class TimestampTests extends BaseTest { * Validate that a NullPointerException is thrown if a null is passed to the * after method */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test22() throws Exception { Timestamp ts1 = Timestamp.valueOf("1966-08-30 08:08:08"); - ts1.after(null); + assertThrows(NullPointerException.class, () -> ts1.after(null)); } /* @@ -476,21 +482,21 @@ public class TimestampTests extends BaseTest { /* * Validate an IllegalArgumentException is thrown for an invalid nanos value */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test38() throws Exception { Timestamp ts1 = Timestamp.valueOf("1961-08-30 00:00:00"); - ts1.setNanos(-1); + assertThrows(IllegalArgumentException.class, () -> ts1.setNanos(-1)); } /* * Validate an IllegalArgumentException is thrown for an invalid nanos value */ - @Test(expectedExceptions = IllegalArgumentException.class) + @Test public void test39() throws Exception { int nanos = 999999999; Timestamp ts1 = Timestamp.valueOf("1961-08-30 00:00:00"); - ts1.setNanos(nanos + 1); + assertThrows(IllegalArgumentException.class, () -> ts1.setNanos(nanos + 1)); } /* @@ -541,10 +547,10 @@ public class TimestampTests extends BaseTest { /* * Validate an NPE occurs when a null LocalDateTime is passed to valueOF */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test44() throws Exception { LocalDateTime ldt = null; - Timestamp.valueOf(ldt); + assertThrows(NullPointerException.class, () -> Timestamp.valueOf(ldt)); } /* @@ -572,10 +578,10 @@ public class TimestampTests extends BaseTest { /* * Validate an NPE occurs when a null instant is passed to from */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test47() throws Exception { Instant instant = null; - Timestamp.from(instant); + assertThrows(NullPointerException.class, () -> Timestamp.from(instant)); } // Added SQE tests @@ -628,7 +634,8 @@ public class TimestampTests extends BaseTest { * Validate that two Timestamps are equal when one is created from the * toString() of the other */ - @Test(dataProvider = "validateNanos") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validateNanos") public void test51(String ts, int nanos) { Timestamp ts1 = Timestamp.valueOf(ts); Timestamp ts2 = Timestamp.valueOf(ts1.toString()); @@ -636,35 +643,36 @@ public class TimestampTests extends BaseTest { "Error with Nanos"); } - @Test(dataProvider = "validTimestampLongValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validTimestampLongValues") public void test52(long value, String ts) { Timestamp ts1 = new Timestamp(value); - assertEquals(ts1.toString(), ts, "ts1.toString() != ts"); + assertEquals(ts, ts1.toString(), "ts1.toString() != ts"); } @Test public void test53() { // The latest Instant that can be converted to a Timestamp. Instant instant1 = Instant.ofEpochSecond(Long.MAX_VALUE / 1000, 999_999_999); - assertEquals(Timestamp.from(instant1).toInstant(), instant1); + assertEquals(instant1, Timestamp.from(instant1).toInstant()); // One nanosecond more, and converting it gets an overflow. Instant instant2 = instant1.plusNanos(1); - expectThrows(IllegalArgumentException.class, () -> Timestamp.from(instant2)); + assertThrows(IllegalArgumentException.class, () -> Timestamp.from(instant2)); // The earliest Instant that can be converted to a Timestamp. Instant instant3 = Instant.ofEpochSecond(Long.MIN_VALUE / 1000, 0); - assertEquals(Timestamp.from(instant3).toInstant(), instant3); + assertEquals(instant3, Timestamp.from(instant3).toInstant()); // One nanosecond less, and converting it gets an overflow. Instant instant4 = instant3.minusNanos(1); - expectThrows(IllegalArgumentException.class, () -> Timestamp.from(instant4)); + assertThrows(IllegalArgumentException.class, () -> Timestamp.from(instant4)); // The latest possible Instant will certainly overflow. - expectThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MAX)); + assertThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MAX)); // The earliest possible Instant will certainly overflow. - expectThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MIN)); + assertThrows(IllegalArgumentException.class, () -> Timestamp.from(Instant.MIN)); } /* @@ -683,7 +691,7 @@ public class TimestampTests extends BaseTest { assertTrue(ts1.equals(ts2)); // As the Timestamp values, including the nanos are the same, the hashCode's // should be equal - assertEquals(ts1.hashCode(), ts2.hashCode()); + assertEquals(ts2.hashCode(), ts1.hashCode()); } /* @@ -702,7 +710,7 @@ public class TimestampTests extends BaseTest { assertTrue(ts2.equals(ts2)); assertFalse(ts1.equals(ts2)); // As the nanos differ, the hashCode values should differ - assertNotEquals(ts1.hashCode(), ts2.hashCode()); + assertNotEquals(ts2.hashCode(), ts1.hashCode()); } /* @@ -710,33 +718,32 @@ public class TimestampTests extends BaseTest { * to validate that an IllegalArgumentException will be thrown from the * valueOf method */ - @DataProvider(name = "invalidTimestampValues") - private Object[][] invalidTimestampValues() { - return new Object[][]{ - {"2009-11-01-01 10:50:01"}, - {"aaaa-11-01-01 10:50"}, - {"aaaa-11-01 10:50"}, - {"1961--30 00:00:00"}, - {"--30 00:00:00"}, - {"-- 00:00:00"}, - {"1961-1- 00:00:00"}, - {"2009-11-01"}, - {"10:50:01"}, - {"1961-a-30 00:00:00"}, - {"1961-01-bb 00:00:00"}, - {"1961-08-30 00:00:00."}, - {"1961-08-30 :00:00"}, - {"1961-08-30 00::00"}, - {"1961-08-30 00:00:"}, - {"1961-08-30 ::"}, - {"1961-08-30 0a:00:00"}, - {"1961-08-30 00:bb:00"}, - {"1961-08-30 00:01:cc"}, - {"1961-08-30 00:00:00.01a"}, - {"1961-08-30 00:00:00.a"}, - {"1996-12-10 12:26:19.1234567890"}, - {null} - }; + private Stream invalidTimestampValues() { + return Stream.of( + "2009-11-01-01 10:50:01", + "aaaa-11-01-01 10:50", + "aaaa-11-01 10:50", + "1961--30 00:00:00", + "--30 00:00:00", + "-- 00:00:00", + "1961-1- 00:00:00", + "2009-11-01", + "10:50:01", + "1961-a-30 00:00:00", + "1961-01-bb 00:00:00", + "1961-08-30 00:00:00.", + "1961-08-30 :00:00", + "1961-08-30 00::00", + "1961-08-30 00:00:", + "1961-08-30 ::", + "1961-08-30 0a:00:00", + "1961-08-30 00:bb:00", + "1961-08-30 00:01:cc", + "1961-08-30 00:00:00.01a", + "1961-08-30 00:00:00.a", + "1996-12-10 12:26:19.1234567890", + null + ); } /* @@ -744,67 +751,65 @@ public class TimestampTests extends BaseTest { * to validate that an IllegalArgumentException will not be thrown from the * valueOf method and the corect value from toString() is returned */ - @DataProvider(name = "validTimestampValues") - private Object[][] validTimestampValues() { - return new Object[][]{ - {"1961-08-30 00:00:00", "1961-08-30 00:00:00.0"}, - {"1961-08-30 11:22:33", "1961-08-30 11:22:33.0"}, - {"1961-8-30 00:00:00", "1961-08-30 00:00:00.0"}, - {"1966-08-1 00:00:00", "1966-08-01 00:00:00.0"}, - {"1996-12-10 12:26:19.1", "1996-12-10 12:26:19.1"}, - {"1996-12-10 12:26:19.12", "1996-12-10 12:26:19.12"}, - {"1996-12-10 12:26:19.123", "1996-12-10 12:26:19.123"}, - {"1996-12-10 12:26:19.1234", "1996-12-10 12:26:19.1234"}, - {"1996-12-10 12:26:19.12345", "1996-12-10 12:26:19.12345"}, - {"1996-12-10 12:26:19.123456", "1996-12-10 12:26:19.123456"}, - {"1996-12-10 12:26:19.1234567", "1996-12-10 12:26:19.1234567"}, - {"1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"}, - {"1996-12-10 12:26:19.123456789", "1996-12-10 12:26:19.123456789"}, - {"1996-12-10 12:26:19.000000001", "1996-12-10 12:26:19.000000001"}, - {"1996-12-10 12:26:19.000000012", "1996-12-10 12:26:19.000000012"}, - {"1996-12-10 12:26:19.000000123", "1996-12-10 12:26:19.000000123"}, - {"1996-12-10 12:26:19.000001234", "1996-12-10 12:26:19.000001234"}, - {"1996-12-10 12:26:19.000012345", "1996-12-10 12:26:19.000012345"}, - {"1996-12-10 12:26:19.000123456", "1996-12-10 12:26:19.000123456"}, - {"1996-12-10 12:26:19.001234567", "1996-12-10 12:26:19.001234567"}, - {"1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"}, - {"1996-12-10 12:26:19.0", "1996-12-10 12:26:19.0"}, - {"1996-12-10 12:26:19.01230", "1996-12-10 12:26:19.0123"} - }; + private Stream validTimestampValues() { + return Stream.of( + Arguments.of("1961-08-30 00:00:00", "1961-08-30 00:00:00.0"), + Arguments.of("1961-08-30 11:22:33", "1961-08-30 11:22:33.0"), + Arguments.of("1961-8-30 00:00:00", "1961-08-30 00:00:00.0"), + Arguments.of("1966-08-1 00:00:00", "1966-08-01 00:00:00.0"), + Arguments.of("1996-12-10 12:26:19.1", "1996-12-10 12:26:19.1"), + Arguments.of("1996-12-10 12:26:19.12", "1996-12-10 12:26:19.12"), + Arguments.of("1996-12-10 12:26:19.123", "1996-12-10 12:26:19.123"), + Arguments.of("1996-12-10 12:26:19.1234", "1996-12-10 12:26:19.1234"), + Arguments.of("1996-12-10 12:26:19.12345", "1996-12-10 12:26:19.12345"), + Arguments.of("1996-12-10 12:26:19.123456", "1996-12-10 12:26:19.123456"), + Arguments.of("1996-12-10 12:26:19.1234567", "1996-12-10 12:26:19.1234567"), + Arguments.of("1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"), + Arguments.of("1996-12-10 12:26:19.123456789", "1996-12-10 12:26:19.123456789"), + Arguments.of("1996-12-10 12:26:19.000000001", "1996-12-10 12:26:19.000000001"), + Arguments.of("1996-12-10 12:26:19.000000012", "1996-12-10 12:26:19.000000012"), + Arguments.of("1996-12-10 12:26:19.000000123", "1996-12-10 12:26:19.000000123"), + Arguments.of("1996-12-10 12:26:19.000001234", "1996-12-10 12:26:19.000001234"), + Arguments.of("1996-12-10 12:26:19.000012345", "1996-12-10 12:26:19.000012345"), + Arguments.of("1996-12-10 12:26:19.000123456", "1996-12-10 12:26:19.000123456"), + Arguments.of("1996-12-10 12:26:19.001234567", "1996-12-10 12:26:19.001234567"), + Arguments.of("1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"), + Arguments.of("1996-12-10 12:26:19.0", "1996-12-10 12:26:19.0"), + Arguments.of("1996-12-10 12:26:19.01230", "1996-12-10 12:26:19.0123") + ); } - @DataProvider(name = "validTimestampLongValues") - private Object[][] validTimestampLongValues() { - return new Object[][]{ - {1L, "1970-01-01 01:00:00.001"}, - {-3600*1000L - 1, "1969-12-31 23:59:59.999"}, - {-(20000L*365*24*60*60*1000), "18018-08-28 01:00:00.0"}, - {Timestamp.valueOf("1961-08-30 11:22:33").getTime(), "1961-08-30 11:22:33.0"}, - {Timestamp.valueOf("1961-08-30 11:22:33.54321000").getTime(), "1961-08-30 11:22:33.543"}, // nanoprecision lost - {new Timestamp(114, 10, 10, 10, 10, 10, 100000000).getTime(), "2014-11-10 10:10:10.1"}, - {new Timestamp(0, 10, 10, 10, 10, 10, 100000).getTime(), "1900-11-10 10:10:10.0"}, // nanoprecision lost - {new Date(114, 10, 10).getTime(), "2014-11-10 00:00:00.0"}, - {new Date(0, 10, 10).getTime(), "1900-11-10 00:00:00.0"}, - {LocalDateTime.of(1960, 10, 10, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) - .toInstant().toEpochMilli(), "1960-10-10 19:10:10.0"}, + private Stream validTimestampLongValues() { + return Stream.of( + Arguments.of(1L, "1970-01-01 01:00:00.001"), + Arguments.of(-3600*1000L - 1, "1969-12-31 23:59:59.999"), + Arguments.of(-(20000L*365*24*60*60*1000), "18018-08-28 01:00:00.0"), + Arguments.of(Timestamp.valueOf("1961-08-30 11:22:33").getTime(), "1961-08-30 11:22:33.0"), + Arguments.of(Timestamp.valueOf("1961-08-30 11:22:33.54321000").getTime(), "1961-08-30 11:22:33.543"), // nanoprecision lost + Arguments.of(new Timestamp(114, 10, 10, 10, 10, 10, 100000000).getTime(), "2014-11-10 10:10:10.1"), + Arguments.of(new Timestamp(0, 10, 10, 10, 10, 10, 100000).getTime(), "1900-11-10 10:10:10.0"), // nanoprecision lost + Arguments.of(new Date(114, 10, 10).getTime(), "2014-11-10 00:00:00.0"), + Arguments.of(new Date(0, 10, 10).getTime(), "1900-11-10 00:00:00.0"), + Arguments.of(LocalDateTime.of(1960, 10, 10, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) + .toInstant().toEpochMilli(), "1960-10-10 19:10:10.0"), // millisecond timestamps wraps around at year 1, so Long.MIN_VALUE looks similar // Long.MAX_VALUE, while actually representing 292278994 BCE - {Long.MIN_VALUE, "292278994-08-17 08:12:55.192"}, - {Long.MAX_VALUE + 1, "292278994-08-17 08:12:55.192"}, - {Long.MAX_VALUE, "292278994-08-17 08:12:55.807"}, - {Long.MIN_VALUE - 1, "292278994-08-17 08:12:55.807"}, + Arguments.of(Long.MIN_VALUE, "292278994-08-17 08:12:55.192"), + Arguments.of(Long.MAX_VALUE + 1, "292278994-08-17 08:12:55.192"), + Arguments.of(Long.MAX_VALUE, "292278994-08-17 08:12:55.807"), + Arguments.of(Long.MIN_VALUE - 1, "292278994-08-17 08:12:55.807"), // wrap around point near 0001-01-01, test that we never get a negative year: - {-(1970L*365*24*60*60*1000), "0001-04-25 01:00:00.0"}, - {-(1970L*365*24*60*60*1000 + 115*24*60*60*1000L), "0001-12-31 01:00:00.0"}, - {-(1970L*365*24*60*60*1000 + 115*24*60*60*1000L - 23*60*60*1000L), "0001-01-01 00:00:00.0"}, + Arguments.of(-(1970L*365*24*60*60*1000), "0001-04-25 01:00:00.0"), + Arguments.of(-(1970L*365*24*60*60*1000 + 115*24*60*60*1000L), "0001-12-31 01:00:00.0"), + Arguments.of(-(1970L*365*24*60*60*1000 + 115*24*60*60*1000L - 23*60*60*1000L), "0001-01-01 00:00:00.0"), - {LocalDateTime.of(0, 1, 1, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) - .toInstant().toEpochMilli() - 2*24*60*60*1000L, "0001-01-01 19:03:08.0"}, // 1 BCE - {LocalDateTime.of(0, 1, 1, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) - .toInstant().toEpochMilli() - 3*24*60*60*1000L, "0002-12-31 19:03:08.0"} // 2 BCE - }; + Arguments.of(LocalDateTime.of(0, 1, 1, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) + .toInstant().toEpochMilli() - 2*24*60*60*1000L, "0001-01-01 19:03:08.0"), // 1 BCE + Arguments.of(LocalDateTime.of(0, 1, 1, 10, 10, 10, 50000).atZone(ZoneId.of("America/Los_Angeles")) + .toInstant().toEpochMilli() - 3*24*60*60*1000L, "0002-12-31 19:03:08.0") // 2 BCE + ); } /* @@ -812,29 +817,28 @@ public class TimestampTests extends BaseTest { * validate that the correct Nanos value is generated from the specified * Timestamp */ - @DataProvider(name = "validateNanos") - private Object[][] validateNanos() { - return new Object[][]{ - {"1961-08-30 00:00:00", 0}, - {"1996-12-10 12:26:19.1", 100000000}, - {"1996-12-10 12:26:19.12", 120000000}, - {"1996-12-10 12:26:19.123", 123000000}, - {"1996-12-10 12:26:19.1234", 123400000}, - {"1996-12-10 12:26:19.12345", 123450000}, - {"1996-12-10 12:26:19.123456", 123456000}, - {"1996-12-10 12:26:19.1234567", 123456700}, - {"1996-12-10 12:26:19.12345678", 123456780}, - {"1996-12-10 12:26:19.123456789", 123456789}, - {"1996-12-10 12:26:19.000000001", 1}, - {"1996-12-10 12:26:19.000000012", 12}, - {"1996-12-10 12:26:19.000000123", 123}, - {"1996-12-10 12:26:19.000001234", 1234}, - {"1996-12-10 12:26:19.000012345", 12345}, - {"1996-12-10 12:26:19.000123456", 123456}, - {"1996-12-10 12:26:19.001234567", 1234567}, - {"1996-12-10 12:26:19.012345678", 12345678}, - {"1996-12-10 12:26:19.0", 0}, - {"1996-12-10 12:26:19.01230", 12300000} - }; + private Stream validateNanos() { + return Stream.of( + Arguments.of("1961-08-30 00:00:00", 0), + Arguments.of("1996-12-10 12:26:19.1", 100000000), + Arguments.of("1996-12-10 12:26:19.12", 120000000), + Arguments.of("1996-12-10 12:26:19.123", 123000000), + Arguments.of("1996-12-10 12:26:19.1234", 123400000), + Arguments.of("1996-12-10 12:26:19.12345", 123450000), + Arguments.of("1996-12-10 12:26:19.123456", 123456000), + Arguments.of("1996-12-10 12:26:19.1234567", 123456700), + Arguments.of("1996-12-10 12:26:19.12345678", 123456780), + Arguments.of("1996-12-10 12:26:19.123456789", 123456789), + Arguments.of("1996-12-10 12:26:19.000000001", 1), + Arguments.of("1996-12-10 12:26:19.000000012", 12), + Arguments.of("1996-12-10 12:26:19.000000123", 123), + Arguments.of("1996-12-10 12:26:19.000001234", 1234), + Arguments.of("1996-12-10 12:26:19.000012345", 12345), + Arguments.of("1996-12-10 12:26:19.000123456", 123456), + Arguments.of("1996-12-10 12:26:19.001234567", 1234567), + Arguments.of("1996-12-10 12:26:19.012345678", 12345678), + Arguments.of("1996-12-10 12:26:19.0", 0), + Arguments.of("1996-12-10 12:26:19.01230", 12300000) + ); } } diff --git a/test/jdk/java/sql/testng/test/sql/othervm/DriverManagerInitTests.java b/test/jdk/java/sql/test/sql/drivermanager/DriverManagerInitTests.java similarity index 93% rename from test/jdk/java/sql/testng/test/sql/othervm/DriverManagerInitTests.java rename to test/jdk/java/sql/test/sql/drivermanager/DriverManagerInitTests.java index d2570993533..f77fde4b166 100644 --- a/test/jdk/java/sql/testng/test/sql/othervm/DriverManagerInitTests.java +++ b/test/jdk/java/sql/test/sql/drivermanager/DriverManagerInitTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 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 @@ -20,7 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.sql.othervm; +package sql.drivermanager; import java.io.BufferedReader; import java.io.CharArrayReader; @@ -33,8 +33,8 @@ import java.util.Enumeration; import java.util.logging.Level; import java.util.logging.Logger; -import static org.testng.Assert.*; -import org.testng.annotations.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class DriverManagerInitTests { diff --git a/test/jdk/java/sql/driverModuleTests/DriverManagerModuleTests.java b/test/jdk/java/sql/test/sql/drivermanager/DriverManagerModuleTests.java similarity index 84% rename from test/jdk/java/sql/driverModuleTests/DriverManagerModuleTests.java rename to test/jdk/java/sql/test/sql/drivermanager/DriverManagerModuleTests.java index aad39cdb1dd..083153ac38f 100644 --- a/test/jdk/java/sql/driverModuleTests/DriverManagerModuleTests.java +++ b/test/jdk/java/sql/test/sql/drivermanager/DriverManagerModuleTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 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 @@ -20,23 +20,19 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package sql.drivermanager; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; /* * @test - * @library /java/sql/modules * @build luckydogdriver/* mystubdriver/* - * @run testng/othervm DriverManagerModuleTests * @summary Tests that a JDBC Driver that is a module can be loaded * via the service-provider loading mechanism. */ @@ -46,28 +42,12 @@ public class DriverManagerModuleTests { private static final String STUBDRIVERURL = "jdbc:stub:myDB"; private static final String CONNECTION_CLASS_NAME = "com.luckydogtennis.StubConnection"; - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @BeforeMethod - public void setUpMethod() throws Exception { - } - - @AfterMethod - public void tearDownMethod() throws Exception { - } - /** * Validate JDBC drivers as modules will be accessible. One driver will be * loaded and registered via the service-provider loading mechanism. The * other driver will need to be explictly loaded * - * @throws java.lang.Exception + * @throws Exception */ @Test public void test() throws Exception { @@ -101,7 +81,7 @@ public class DriverManagerModuleTests { * Validate that a Connection can be obtained from a JDBC driver which is a * module and loaded via the service-provider loading mechanism. * - * @throws java.lang.Exception + * @throws Exception */ @Test public void test00() throws Exception { diff --git a/test/jdk/java/sql/test/sql/drivermanager/TEST.properties b/test/jdk/java/sql/test/sql/drivermanager/TEST.properties new file mode 100644 index 00000000000..0fed686adad --- /dev/null +++ b/test/jdk/java/sql/test/sql/drivermanager/TEST.properties @@ -0,0 +1,4 @@ +# drivermanager tests are run in othervm +othervm.dirs = . +# Required by DriverManagerModuleTests.java +lib.dirs = /java/sql/modules diff --git a/test/jdk/java/sql/testng/TEST.properties b/test/jdk/java/sql/testng/TEST.properties deleted file mode 100644 index 66b1566374a..00000000000 --- a/test/jdk/java/sql/testng/TEST.properties +++ /dev/null @@ -1,4 +0,0 @@ -# JDBC unit tests uses TestNG -TestNG.dirs = . -othervm.dirs = test/sql/othervm - diff --git a/test/jdk/java/sql/testng/util/BaseTest.java b/test/jdk/java/sql/util/BaseTest.java similarity index 54% rename from test/jdk/java/sql/testng/util/BaseTest.java rename to test/jdk/java/sql/util/BaseTest.java index 8751183726d..1a1b14e795a 100644 --- a/test/jdk/java/sql/testng/util/BaseTest.java +++ b/test/jdk/java/sql/util/BaseTest.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 @@ -29,9 +29,12 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.sql.JDBCType; import java.sql.SQLException; +import java.util.stream.Stream; -import org.testng.annotations.DataProvider; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.provider.Arguments; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class BaseTest { protected final String reason = "reason"; @@ -73,29 +76,11 @@ public class BaseTest { return o1; } - /* - * DataProvider used to specify the value to set and check for - * methods using boolean values - */ - @DataProvider(name = "trueFalse") - protected Object[][] trueFalse() { - return new Object[][]{ - {true}, - {false} - }; - } - /* * DataProvider used to specify the standard JDBC Types */ - @DataProvider(name = "jdbcTypes") - protected Object[][] jdbcTypes() { - Object[][] o = new Object[JDBCType.values().length][1]; - int pos = 0; - for (JDBCType c : JDBCType.values()) { - o[pos++][0] = c.getVendorTypeNumber(); - } - return o; + protected Stream jdbcTypes() { + return Stream.of(JDBCType.values()).map(JDBCType::getVendorTypeNumber); } /* @@ -103,15 +88,14 @@ public class BaseTest { * that enquoteLiteral converts a string to a literal and every instance of * a single quote will be converted into two single quotes in the literal. */ - @DataProvider(name = "validEnquotedLiteralValues") - protected Object[][] validEnquotedLiteralValues() { - return new Object[][]{ - {"Hello", "'Hello'"}, - {"G'Day", "'G''Day'"}, - {"'G''Day'", "'''G''''Day'''"}, - {"I'''M", "'I''''''M'"}, - {"The Dark Knight", "'The Dark Knight'"}, - }; + protected Stream validEnquotedLiteralValues() { + return Stream.of( + Arguments.of("Hello", "'Hello'"), + Arguments.of("G'Day", "'G''Day'"), + Arguments.of("'G''Day'", "'''G''''Day'''"), + Arguments.of("I'''M", "'I''''''M'"), + Arguments.of("The Dark Knight", "'The Dark Knight'") + ); } /* @@ -119,37 +103,37 @@ public class BaseTest { * that enqouteIdentifier returns a simple SQL Identifier or a * quoted identifier */ - @DataProvider(name = "validIdentifierValues") - protected Object[][] validEnquotedIdentifierValues() { - return new Object[][]{ - {"b", false, "b"}, - {"b", true, "\"b\""}, - {MAX_LENGTH_IDENTIFIER, false, MAX_LENGTH_IDENTIFIER}, - {MAX_LENGTH_IDENTIFIER, true, "\"" + MAX_LENGTH_IDENTIFIER + "\""}, - {"Hello", false, "Hello"}, - {"Hello", true, "\"Hello\""}, - {"G'Day", false, "\"G'Day\""}, - {"G'Day", true, "\"G'Day\""}, - {"Bruce Wayne", false, "\"Bruce Wayne\""}, - {"Bruce Wayne", true, "\"Bruce Wayne\""}, - {"select", false, "\"select\""}, - {"table", true, "\"table\""}, - {"GoodDay$", false, "\"GoodDay$\""}, - {"GoodDay$", true, "\"GoodDay$\""},}; + protected Stream validEnquotedIdentifierValues() { + return Stream.of( + Arguments.of("b", false, "b"), + Arguments.of("b", true, "\"b\""), + Arguments.of(MAX_LENGTH_IDENTIFIER, false, MAX_LENGTH_IDENTIFIER), + Arguments.of(MAX_LENGTH_IDENTIFIER, true, "\"" + MAX_LENGTH_IDENTIFIER + "\""), + Arguments.of("Hello", false, "Hello"), + Arguments.of("Hello", true, "\"Hello\""), + Arguments.of("G'Day", false, "\"G'Day\""), + Arguments.of("G'Day", true, "\"G'Day\""), + Arguments.of("Bruce Wayne", false, "\"Bruce Wayne\""), + Arguments.of("Bruce Wayne", true, "\"Bruce Wayne\""), + Arguments.of("select", false, "\"select\""), + Arguments.of("table", true, "\"table\""), + Arguments.of("GoodDay$", false, "\"GoodDay$\""), + Arguments.of("GoodDay$", true, "\"GoodDay$\"") + ); } /* * DataProvider used to provide strings are invalid for enquoteIdentifier * resulting in a SQLException being thrown */ - @DataProvider(name = "invalidIdentifierValues") - protected Object[][] invalidEnquotedIdentifierValues() { - return new Object[][]{ - {"Hel\"lo", false}, - {"\"Hel\"lo\"", true}, - {"Hello" + '\0', false}, - {"", false}, - {MAX_LENGTH_IDENTIFIER + 'a', false},}; + protected Stream invalidEnquotedIdentifierValues() { + return Stream.of( + Arguments.of("Hel\"lo", false), + Arguments.of("\"Hel\"lo\"", true), + Arguments.of("Hello" + '\0', false), + Arguments.of("", false), + Arguments.of(MAX_LENGTH_IDENTIFIER + 'a', false) + ); } /* @@ -157,22 +141,21 @@ public class BaseTest { * that isSimpleIdentifier returns the correct value based on the * identifier specified. */ - @DataProvider(name = "simpleIdentifierValues") - protected Object[][] simpleIdentifierValues() { - return new Object[][]{ - {"b", true}, - {"Hello", true}, - {"\"Gotham\"", false}, - {"G'Day", false}, - {"Bruce Wayne", false}, - {"GoodDay$", false}, - {"Dick_Grayson", true}, - {"Batmobile1966", true}, - {MAX_LENGTH_IDENTIFIER, true}, - {MAX_LENGTH_IDENTIFIER + 'a', false}, - {"", false}, - {"select", false} - }; + protected Stream simpleIdentifierValues() { + return Stream.of( + Arguments.of("b", true), + Arguments.of("Hello", true), + Arguments.of("\"Gotham\"", false), + Arguments.of("G'Day", false), + Arguments.of("Bruce Wayne", false), + Arguments.of("GoodDay$", false), + Arguments.of("Dick_Grayson", true), + Arguments.of("Batmobile1966", true), + Arguments.of(MAX_LENGTH_IDENTIFIER, true), + Arguments.of(MAX_LENGTH_IDENTIFIER + 'a', false), + Arguments.of("", false), + Arguments.of("select", false) + ); } /* @@ -181,15 +164,14 @@ public class BaseTest { * literal and every instance of * a single quote will be converted into two single quotes in the literal. */ - @DataProvider(name = "validEnquotedNCharLiteralValues") - protected Object[][] validEnquotedNCharLiteralValues() { - return new Object[][]{ - {"Hello", "N'Hello'"}, - {"G'Day", "N'G''Day'"}, - {"'G''Day'", "N'''G''''Day'''"}, - {"I'''M", "N'I''''''M'"}, - {"N'Hello'", "N'N''Hello'''"}, - {"The Dark Knight", "N'The Dark Knight'"} - }; + protected Stream validEnquotedNCharLiteralValues() { + return Stream.of( + Arguments.of("Hello", "N'Hello'"), + Arguments.of("G'Day", "N'G''Day'"), + Arguments.of("'G''Day'", "N'''G''''Day'''"), + Arguments.of("I'''M", "N'I''''''M'"), + Arguments.of("N'Hello'", "N'N''Hello'''"), + Arguments.of("The Dark Knight", "N'The Dark Knight'") + ); } } diff --git a/test/jdk/java/sql/testng/util/DriverActionImpl.java b/test/jdk/java/sql/util/DriverActionImpl.java similarity index 94% rename from test/jdk/java/sql/testng/util/DriverActionImpl.java rename to test/jdk/java/sql/util/DriverActionImpl.java index 4a286ad8f3c..48fed8086a7 100644 --- a/test/jdk/java/sql/testng/util/DriverActionImpl.java +++ b/test/jdk/java/sql/util/DriverActionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/java/sql/testng/util/SerializedBatchUpdateException.java b/test/jdk/java/sql/util/SerializedBatchUpdateException.java similarity index 99% rename from test/jdk/java/sql/testng/util/SerializedBatchUpdateException.java rename to test/jdk/java/sql/util/SerializedBatchUpdateException.java index 00efc5275a7..08473e6a184 100644 --- a/test/jdk/java/sql/testng/util/SerializedBatchUpdateException.java +++ b/test/jdk/java/sql/util/SerializedBatchUpdateException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/java/sql/testng/util/StubCallableStatement.java b/test/jdk/java/sql/util/StubCallableStatement.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubCallableStatement.java rename to test/jdk/java/sql/util/StubCallableStatement.java index 4a7c27314bb..4700699c4ba 100644 --- a/test/jdk/java/sql/testng/util/StubCallableStatement.java +++ b/test/jdk/java/sql/util/StubCallableStatement.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 diff --git a/test/jdk/java/sql/testng/util/StubConnection.java b/test/jdk/java/sql/util/StubConnection.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubConnection.java rename to test/jdk/java/sql/util/StubConnection.java index cd013572adc..e1a60efc323 100644 --- a/test/jdk/java/sql/testng/util/StubConnection.java +++ b/test/jdk/java/sql/util/StubConnection.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 diff --git a/test/jdk/java/sql/testng/util/StubDatabaseMetaData.java b/test/jdk/java/sql/util/StubDatabaseMetaData.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubDatabaseMetaData.java rename to test/jdk/java/sql/util/StubDatabaseMetaData.java index 3bf70afaa8f..3c95ac94793 100644 --- a/test/jdk/java/sql/testng/util/StubDatabaseMetaData.java +++ b/test/jdk/java/sql/util/StubDatabaseMetaData.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 diff --git a/test/jdk/java/sql/testng/util/StubDriver.java b/test/jdk/java/sql/util/StubDriver.java similarity index 96% rename from test/jdk/java/sql/testng/util/StubDriver.java rename to test/jdk/java/sql/util/StubDriver.java index 12253080377..c45b9ec9919 100644 --- a/test/jdk/java/sql/testng/util/StubDriver.java +++ b/test/jdk/java/sql/util/StubDriver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/java/sql/testng/util/StubDriverDA.java b/test/jdk/java/sql/util/StubDriverDA.java similarity index 96% rename from test/jdk/java/sql/testng/util/StubDriverDA.java rename to test/jdk/java/sql/util/StubDriverDA.java index 5b23f0846d8..f134a9d28cf 100644 --- a/test/jdk/java/sql/testng/util/StubDriverDA.java +++ b/test/jdk/java/sql/util/StubDriverDA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/java/sql/testng/util/StubPreparedStatement.java b/test/jdk/java/sql/util/StubPreparedStatement.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubPreparedStatement.java rename to test/jdk/java/sql/util/StubPreparedStatement.java index a3b95a65f4e..fdb90df4797 100644 --- a/test/jdk/java/sql/testng/util/StubPreparedStatement.java +++ b/test/jdk/java/sql/util/StubPreparedStatement.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 diff --git a/test/jdk/java/sql/testng/util/StubStatement.java b/test/jdk/java/sql/util/StubStatement.java similarity index 99% rename from test/jdk/java/sql/testng/util/StubStatement.java rename to test/jdk/java/sql/util/StubStatement.java index c8f6a3ac071..00ed6268856 100644 --- a/test/jdk/java/sql/testng/util/StubStatement.java +++ b/test/jdk/java/sql/util/StubStatement.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 diff --git a/test/jdk/javax/sql/testng/TEST.properties b/test/jdk/javax/sql/TEST.properties similarity index 68% rename from test/jdk/javax/sql/testng/TEST.properties rename to test/jdk/javax/sql/TEST.properties index cb97c160507..d9ed805344a 100644 --- a/test/jdk/javax/sql/testng/TEST.properties +++ b/test/jdk/javax/sql/TEST.properties @@ -1,7 +1,7 @@ -# JDBC unit tests uses TestNG -TestNG.dirs= . +# JDBC unit tests uses JUnit +JUnit.dirs= . othervm.dirs= . -lib.dirs = /java/sql/testng +lib.dirs = /java/sql/util modules = java.sql.rowset/com.sun.rowset \ java.sql.rowset/com.sun.rowset.internal \ java.sql.rowset/com.sun.rowset.providers diff --git a/test/jdk/javax/sql/testng/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory b/test/jdk/javax/sql/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory similarity index 100% rename from test/jdk/javax/sql/testng/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory rename to test/jdk/javax/sql/jars/badFactory/META-INF/services/javax.sql.rowset.RowSetFactory diff --git a/test/jdk/javax/sql/testng/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory b/test/jdk/javax/sql/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory similarity index 100% rename from test/jdk/javax/sql/testng/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory rename to test/jdk/javax/sql/jars/goodFactory/META-INF/services/javax.sql.rowset.RowSetFactory diff --git a/test/jdk/javax/sql/rowset/TEST.properties b/test/jdk/javax/sql/rowset/TEST.properties deleted file mode 100644 index 6c5a2c7aceb..00000000000 --- a/test/jdk/javax/sql/rowset/TEST.properties +++ /dev/null @@ -1 +0,0 @@ -modules = java.sql.rowset diff --git a/test/jdk/javax/sql/rowset/serial/SerialBlob/SetBinaryStream.java b/test/jdk/javax/sql/rowset/serial/SerialBlob/SetBinaryStream.java deleted file mode 100644 index a2d7ea2f8cb..00000000000 --- a/test/jdk/javax/sql/rowset/serial/SerialBlob/SetBinaryStream.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import javax.sql.rowset.serial.SerialBlob; -import javax.sql.rowset.serial.SerialException; - -/** - * @test - * @bug 7077451 - * @summary tests if the correct exception is thrown when calling method setBinaryStream() on SerialBlob - */ -public class SetBinaryStream { - - public static void main(String[] args) throws Exception { - SerialBlob blob = new SerialBlob(new byte[0]); - try { - blob.setBinaryStream(0); - } catch (SerialException e) { - System.out.println("Test PASSED"); - } - } - -} diff --git a/test/jdk/javax/sql/rowset/serial/SerialClob/SetAsciiStream.java b/test/jdk/javax/sql/rowset/serial/SerialClob/SetAsciiStream.java deleted file mode 100644 index 10deaf523e9..00000000000 --- a/test/jdk/javax/sql/rowset/serial/SerialClob/SetAsciiStream.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import javax.sql.rowset.serial.SerialClob; -import javax.sql.rowset.serial.SerialException; - -/** - * @test - * @bug 7077451 - * @summary tests if the correct exception is thrown when calling method setAsciiStream() on SerialClob - */ -public class SetAsciiStream { - - public static void main(String[] args) throws Exception { - SerialClob clob = new SerialClob(new char[0]); - try { - clob.setAsciiStream(0); - } catch (SerialException e) { - System.out.println("Test PASSED"); - } - } - -} diff --git a/test/jdk/javax/sql/rowset/serial/SerialClob/SetCharacterStream.java b/test/jdk/javax/sql/rowset/serial/SerialClob/SetCharacterStream.java deleted file mode 100644 index be861e43723..00000000000 --- a/test/jdk/javax/sql/rowset/serial/SerialClob/SetCharacterStream.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -import javax.sql.rowset.serial.SerialClob; -import javax.sql.rowset.serial.SerialException; - -/** - * @test - * @bug 7077451 - * @summary tests if the correct exception is thrown when calling method setCharacterStream() on SerialClob - */ -public class SetCharacterStream { - - public static void main(String[] args) throws Exception { - SerialClob clob = new SerialClob(new char[0]); - try { - clob.setCharacterStream(0); - } catch (SerialException e) { - System.out.println("Test PASSED"); - } - } - -} diff --git a/test/jdk/javax/sql/testng/test/rowset/BaseRowSetTests.java b/test/jdk/javax/sql/test/rowset/BaseRowSetTests.java similarity index 83% rename from test/jdk/javax/sql/testng/test/rowset/BaseRowSetTests.java rename to test/jdk/javax/sql/test/rowset/BaseRowSetTests.java index c1d2d9a2ed0..8c47415db39 100644 --- a/test/jdk/javax/sql/testng/test/rowset/BaseRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/BaseRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, 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 @@ -40,14 +40,19 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Calendar; +import java.util.stream.Stream; import javax.sql.RowSet; import javax.sql.rowset.serial.SerialArray; import javax.sql.rowset.serial.SerialBlob; import javax.sql.rowset.serial.SerialClob; import javax.sql.rowset.serial.SerialRef; -import static org.testng.Assert.*; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.StubArray; import util.StubBaseRowSet; import util.StubBlob; @@ -67,7 +72,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyCursorMoved is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0000(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -78,7 +84,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowChanged is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0001(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -89,7 +96,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowSetChanged is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0002(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -101,7 +109,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create multiple RowSetListeners and validate that notifyRowSetChanged * is called on all listeners */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0003(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -116,7 +125,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create multiple RowSetListeners and validate that notifyRowChanged * is called on all listeners */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0004(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -131,7 +141,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create multiple RowSetListeners and validate that notifyCursorMoved * is called on all listeners */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0005(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -146,7 +157,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create a RowSetListener and validate that notifyRowSetChanged, * notifyRowChanged() and notifyCursorMoved are called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0006(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -163,7 +175,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Create multiple RowSetListeners and validate that notifyRowSetChanged, * notifyRowChanged() and notifyCursorMoved are called on all listeners */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0007(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -185,7 +198,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * remove the listener, invoke notifyRowSetChanged again and verify the * listner is not called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0008(StubBaseRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -202,7 +216,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Set the base parameters and validate that the value set is * the correct type and value */ - @Test(dataProvider = "testBaseParameters") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("testBaseParameters") public void baseRowSetTest0009(int pos, Object o) throws Exception { assertTrue(getParam(pos, o).getClass().isInstance(o)); assertTrue(o.equals(getParam(pos, o))); @@ -212,7 +227,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Set the complex parameters and validate that the value set is * the correct type */ - @Test(dataProvider = "testAdvancedParameters") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("testAdvancedParameters") public void baseRowSetTest0010(int pos, Object o) throws Exception { assertTrue(getParam(pos, o).getClass().isInstance(o)); } @@ -220,7 +236,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Validate setNull specifying the supported type values */ - @Test(dataProvider = "jdbcTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("jdbcTypes") public void baseRowSetTest0011(Integer type) throws Exception { brs = new StubBaseRowSet(); brs.setNull(1, type); @@ -231,7 +248,8 @@ public class BaseRowSetTests extends CommonRowSetTests { * Validate setNull specifying the supported type values and that * typeName is set internally */ - @Test(dataProvider = "jdbcTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("jdbcTypes") public void baseRowSetTest0012(Integer type) throws Exception { brs = new StubBaseRowSet(); brs.setNull(1, type, "SUPERHERO"); @@ -274,7 +292,8 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * Validate that initParams() initializes the parameters */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void baseRowSetTest0016(StubBaseRowSet rs) throws Exception { rs.setInt(1, 1); rs.initParams(); @@ -285,8 +304,7 @@ public class BaseRowSetTests extends CommonRowSetTests { /* * DataProvider used to set parameters for basic types that are supported */ - @DataProvider(name = "testBaseParameters") - private Object[][] testBaseParameters() throws SQLException { + private Stream testBaseParameters() throws SQLException { Integer aInt = 1; Long aLong = Long.MAX_VALUE; Short aShort = Short.MIN_VALUE; @@ -320,34 +338,32 @@ public class BaseRowSetTests extends CommonRowSetTests { brs.setObject(17, query, Types.CHAR); brs.setObject(18, query, Types.CHAR, 0); - return new Object[][]{ - {1, aInt}, - {2, query}, - {3, aLong}, - {4, aBoolean}, - {5, aShort}, - {6, aDouble}, - {7, bd}, - {8, aFloat}, - {9, aByte}, - {10, aDate}, - {11, aTime}, - {12, aTimeStamp}, - {13, aDate}, - {14, aTime}, - {15, aTimeStamp}, - {16, query}, - {17, query}, - {18, query} - - }; + return Stream.of( + Arguments.of(1, aInt), + Arguments.of(2, query), + Arguments.of(3, aLong), + Arguments.of(4, aBoolean), + Arguments.of(5, aShort), + Arguments.of(6, aDouble), + Arguments.of(7, bd), + Arguments.of(8, aFloat), + Arguments.of(9, aByte), + Arguments.of(10, aDate), + Arguments.of(11, aTime), + Arguments.of(12, aTimeStamp), + Arguments.of(13, aDate), + Arguments.of(14, aTime), + Arguments.of(15, aTimeStamp), + Arguments.of(16, query), + Arguments.of(17, query), + Arguments.of(18, query) + ); } /* * DataProvider used to set advanced parameters for types that are supported */ - @DataProvider(name = "testAdvancedParameters") - private Object[][] testAdvancedParameters() throws SQLException { + private Stream testAdvancedParameters() throws SQLException { byte[] bytes = new byte[10]; Ref aRef = new SerialRef(new StubRef("INTEGER", query)); @@ -367,17 +383,17 @@ public class BaseRowSetTests extends CommonRowSetTests { brs.setUnicodeStream(8, is, query.length()); brs.setCharacterStream(9, rdr, query.length()); - return new Object[][]{ - {1, bytes}, - {2, is}, - {3, aRef}, - {4, aArray}, - {5, aBlob}, - {6, aClob}, - {7, is}, - {8, is}, - {9, rdr} - }; + return Stream.of( + Arguments.of(1, bytes), + Arguments.of(2, is), + Arguments.of(3, aRef), + Arguments.of(4, aArray), + Arguments.of(5, aBlob), + Arguments.of(6, aClob), + Arguments.of(7, is), + Arguments.of(8, is), + Arguments.of(9, rdr) + ); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/CommonRowSetTests.java b/test/jdk/javax/sql/test/rowset/CommonRowSetTests.java similarity index 67% rename from test/jdk/javax/sql/testng/test/rowset/CommonRowSetTests.java rename to test/jdk/javax/sql/test/rowset/CommonRowSetTests.java index 89492aefed5..5e8163fbf05 100644 --- a/test/jdk/javax/sql/testng/test/rowset/CommonRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/CommonRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 @@ -43,17 +43,23 @@ import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import javax.sql.RowSet; import javax.sql.rowset.BaseRowSet; import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.RowSetFactory; import javax.sql.rowset.RowSetMetaDataImpl; import javax.sql.rowset.RowSetProvider; -import org.testng.Assert; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; import util.StubBlob; import util.StubClob; @@ -98,7 +104,7 @@ public abstract class CommonRowSetTests extends BaseTest { try { rsf = RowSetProvider.newFactory(); } catch (SQLException ex) { - Assert.fail(ex.getMessage()); + fail(ex.getMessage()); } } @@ -111,84 +117,76 @@ public abstract class CommonRowSetTests extends BaseTest { * DataProvider used to specify the value to set and check for the * methods for fetch direction */ - @DataProvider(name = "rowSetFetchDirection") - protected Object[][] rowSetFetchDirection() throws Exception { + protected Stream rowSetFetchDirection() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, ResultSet.FETCH_FORWARD}, - {rs, ResultSet.FETCH_REVERSE}, - {rs, ResultSet.FETCH_UNKNOWN} - }; + return Stream.of( + Arguments.of(rs, ResultSet.FETCH_FORWARD), + Arguments.of(rs, ResultSet.FETCH_REVERSE), + Arguments.of(rs, ResultSet.FETCH_UNKNOWN) + ); } /* * DataProvider used to specify the value to set and check for the * methods for Cursor Scroll Type */ - @DataProvider(name = "rowSetScrollTypes") - protected Object[][] rowSetScrollTypes() throws Exception { + protected Stream rowSetScrollTypes() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, ResultSet.TYPE_FORWARD_ONLY}, - {rs, ResultSet.TYPE_SCROLL_INSENSITIVE}, - {rs, ResultSet.TYPE_SCROLL_SENSITIVE} - }; + return Stream.of( + Arguments.of(rs, ResultSet.TYPE_FORWARD_ONLY), + Arguments.of(rs, ResultSet.TYPE_SCROLL_INSENSITIVE), + Arguments.of(rs, ResultSet.TYPE_SCROLL_SENSITIVE) + ); } /* * DataProvider used to specify the value to set and check for * methods using transaction isolation types */ - @DataProvider(name = "rowSetIsolationTypes") - protected Object[][] rowSetIsolationTypes() throws Exception { + protected Stream rowSetIsolationTypes() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, Connection.TRANSACTION_NONE}, - {rs, Connection.TRANSACTION_READ_COMMITTED}, - {rs, Connection.TRANSACTION_READ_UNCOMMITTED}, - {rs, Connection.TRANSACTION_REPEATABLE_READ}, - {rs, Connection.TRANSACTION_SERIALIZABLE} - }; + return Stream.of( + Arguments.of(rs, Connection.TRANSACTION_NONE), + Arguments.of(rs, Connection.TRANSACTION_READ_COMMITTED), + Arguments.of(rs, Connection.TRANSACTION_READ_UNCOMMITTED), + Arguments.of(rs, Connection.TRANSACTION_REPEATABLE_READ), + Arguments.of(rs, Connection.TRANSACTION_SERIALIZABLE) + ); } /* * DataProvider used to specify the value to set and check for the * methods for Concurrency */ - @DataProvider(name = "rowSetConcurrencyTypes") - protected Object[][] rowSetConcurrencyTypes() throws Exception { + protected Stream rowSetConcurrencyTypes() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, ResultSet.CONCUR_READ_ONLY}, - {rs, ResultSet.CONCUR_UPDATABLE} - }; + return Stream.of( + Arguments.of(rs, ResultSet.CONCUR_READ_ONLY), + Arguments.of(rs, ResultSet.CONCUR_UPDATABLE) + ); } /* * DataProvider used to specify the value to set and check for * methods using boolean values */ - @DataProvider(name = "rowSetTrueFalse") - protected Object[][] rowSetTrueFalse() throws Exception { + protected Stream rowSetTrueFalse() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs, true}, - {rs, false} - }; + return Stream.of( + Arguments.of(rs, true), + Arguments.of(rs, false) + ); } /* * DataProvider used to specify the type of RowSet to use. We also must * initialize the RowSet */ - @DataProvider(name = "rowSetType") - protected Object[][] rowSetType() throws Exception { + protected Stream rowSetType() throws Exception { RowSet rs = newInstance(); - return new Object[][]{ - {rs} - }; + return Stream.of(rs); } /* @@ -503,7 +501,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getCommand() returns null by default */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0000(RowSet rs) { assertNull(rs.getCommand()); } @@ -511,7 +510,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getCommand() returns command specified to setCommand */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0001(RowSet rs) throws Exception { rs.setCommand(query); assertTrue(rs.getCommand().equals(query)); @@ -521,7 +521,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getCurrency() returns the correct default value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0002(RowSet rs) throws Exception { assertTrue(rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE); } @@ -530,7 +531,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getCurrency() returns the correct value * after a call to setConcurrency()) */ - @Test(dataProvider = "rowSetConcurrencyTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetConcurrencyTypes") public void commonRowSetTest0003(RowSet rs, int concurType) throws Exception { rs.setConcurrency(concurType); assertTrue(rs.getConcurrency() == concurType); @@ -539,15 +541,17 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getCurrency() throws a SQLException for an invalid value */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0004(RowSet rs) throws Exception { - rs.setConcurrency(ResultSet.CLOSE_CURSORS_AT_COMMIT); + assertThrows(SQLException.class, () -> rs.setConcurrency(ResultSet.CLOSE_CURSORS_AT_COMMIT)); } /* * Validate that getDataSourceName() returns null by default */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0005(RowSet rs) throws Exception { assertTrue(rs.getDataSourceName() == null); } @@ -556,7 +560,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getDataSourceName() returns the value specified * by setDataSourceName() and getUrl() returns null */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0006(RowSet rs) throws Exception { rs.setUrl(url); rs.setDataSourceName(dsName); @@ -568,16 +573,20 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that setDataSourceName() throws a SQLException for an empty * String specified for the data source name */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0007(RowSet rs) throws Exception { - String dsname = ""; - rs.setDataSourceName(dsname); + assertThrows(SQLException.class, () -> { + String dsname = ""; + rs.setDataSourceName(dsname); + }); } /* * Validate that getEscapeProcessing() returns false by default */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0008(RowSet rs) throws Exception { assertTrue(rs.getEscapeProcessing()); } @@ -586,7 +595,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getEscapeProcessing() returns value set by * setEscapeProcessing() */ - @Test(dataProvider = "rowSetTrueFalse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetTrueFalse") public void commonRowSetTest0009(RowSet rs, boolean val) throws Exception { rs.setEscapeProcessing(val); assertTrue(rs.getEscapeProcessing() == val); @@ -595,7 +605,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getFetchDirection() returns the correct default value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0010(RowSet rs) throws Exception { assertTrue(rs.getFetchDirection() == ResultSet.FETCH_FORWARD); } @@ -604,7 +615,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getFetchDirection() returns the value set by * setFetchDirection() */ - @Test(dataProvider = "rowSetFetchDirection") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetFetchDirection") public void commonRowSetTest0011(RowSet rs, int direction) throws Exception { rs.setFetchDirection(direction); assertTrue(rs.getFetchDirection() == direction); @@ -613,26 +625,31 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that setFetchSize() throws a SQLException for an invalid value */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0013(RowSet rs) throws Exception { - rs.setFetchSize(-1); + assertThrows(SQLException.class, () -> rs.setFetchSize(-1)); } /* * Validate that setFetchSize() throws a SQLException for a * value greater than getMaxRows() */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0014(RowSet rs) throws Exception { - rs.setMaxRows(5); - rs.setFetchSize(rs.getMaxRows() + 1); + assertThrows(SQLException.class, () -> { + rs.setMaxRows(5); + rs.setFetchSize(rs.getMaxRows() + 1); + }); } /* * Validate that getFetchSize() returns the correct value after * setFetchSize() has been called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0015(RowSet rs) throws Exception { int maxRows = 150; rs.setFetchSize(0); @@ -647,16 +664,18 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that setMaxFieldSize() throws a SQLException for an invalid value */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0016(RowSet rs) throws Exception { - rs.setMaxFieldSize(-1); + assertThrows(SQLException.class, () -> rs.setMaxFieldSize(-1)); } /* * Validate that getMaxFieldSize() returns the value set by * setMaxFieldSize() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0017(RowSet rs) throws Exception { rs.setMaxFieldSize(0); assertTrue(rs.getMaxFieldSize() == 0); @@ -670,7 +689,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that isReadOnly() returns value set by * setReadOnly() */ - @Test(dataProvider = "rowSetTrueFalse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetTrueFalse") public void commonRowSetTest0018(RowSet rs, boolean val) throws Exception { rs.setReadOnly(val); assertTrue(rs.isReadOnly() == val); @@ -680,7 +700,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getTransactionIsolation() returns value set by * setTransactionIsolation() */ - @Test(dataProvider = "rowSetIsolationTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetIsolationTypes") public void commonRowSetTest0019(RowSet rs, int val) throws Exception { rs.setTransactionIsolation(val); assertTrue(rs.getTransactionIsolation() == val); @@ -689,7 +710,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Validate that getType() returns value set by setType() */ - @Test(dataProvider = "rowSetScrollTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetScrollTypes") public void commonRowSetTest0020(RowSet rs, int val) throws Exception { rs.setType(val); assertTrue(rs.getType() == val); @@ -699,7 +721,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getEscapeProcessing() returns value set by * setEscapeProcessing() */ - @Test(dataProvider = "rowSetTrueFalse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetTrueFalse") public void commonRowSetTest0021(BaseRowSet rs, boolean val) throws Exception { rs.setShowDeleted(val); assertTrue(rs.getShowDeleted() == val); @@ -709,7 +732,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getTypeMap() returns same value set by * setTypeMap() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0022(RowSet rs) throws Exception { Map> map = new HashMap<>(); map.put("SUPERHERO", Class.forName("util.SuperHero")); @@ -721,7 +745,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getUsername() returns same value set by * setUsername() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0023(RowSet rs) throws Exception { rs.setUsername(user); assertTrue(rs.getUsername().equals(user)); @@ -731,7 +756,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getPassword() returns same password set by * setPassword() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0024(RowSet rs) throws Exception { rs.setPassword(password); assertTrue(rs.getPassword().equals(password)); @@ -741,7 +767,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getQueryTimeout() returns same value set by * setQueryTimeout() and that 0 is a valid timeout value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0025(RowSet rs) throws Exception { int timeout = 0; rs.setQueryTimeout(timeout); @@ -752,7 +779,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getQueryTimeout() returns same value set by * setQueryTimeout() and that 0 is a valid timeout value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0026(RowSet rs) throws Exception { int timeout = 10000; rs.setQueryTimeout(timeout); @@ -763,9 +791,10 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that setQueryTimeout() throws a SQLException for a timeout * value < 0 */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0027(RowSet rs) throws Exception { - rs.setQueryTimeout(-1); + assertThrows(SQLException.class, () -> rs.setQueryTimeout(-1)); } @@ -773,7 +802,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate addRowSetListener does not throw an Exception when null is * passed as the parameter */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0028(RowSet rs) throws Exception { rs.addRowSetListener(null); } @@ -782,7 +812,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate removeRowSetListener does not throw an Exception when null is * passed as the parameter */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0029(RowSet rs) throws Exception { rs.removeRowSetListener(null); } @@ -790,7 +821,8 @@ public abstract class CommonRowSetTests extends BaseTest { /* * Set two parameters and then validate clearParameters() will clear them */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0030(BaseRowSet rs) throws Exception { rs.setInt(1, 1); rs.setString(2, query); @@ -803,7 +835,8 @@ public abstract class CommonRowSetTests extends BaseTest { * Validate that getURL() returns same value set by * setURL() */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0031(RowSet rs) throws Exception { rs.setUrl(url); assertTrue(rs.getUrl().equals(url)); @@ -813,560 +846,614 @@ public abstract class CommonRowSetTests extends BaseTest { * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0100(RowSet rs) throws Exception { - InputStream is = null; - rs.setAsciiStream(1, is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setAsciiStream(1, is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0101(RowSet rs) throws Exception { - InputStream is = null; - rs.setAsciiStream("one", is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setAsciiStream("one", is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0102(RowSet rs) throws Exception { - InputStream is = null; - rs.setAsciiStream("one", is, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setAsciiStream("one", is, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0103(RowSet rs) throws Exception { - InputStream is = null; - rs.setBinaryStream(1, is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBinaryStream(1, is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0104(RowSet rs) throws Exception { - InputStream is = null; - rs.setBinaryStream("one", is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBinaryStream("one", is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0105(RowSet rs) throws Exception { - InputStream is = null; - rs.setBinaryStream("one", is, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBinaryStream("one", is, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0106(RowSet rs) throws Exception { - rs.setBigDecimal("one", BigDecimal.ONE); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setBigDecimal("one", BigDecimal.ONE)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0107(RowSet rs) throws Exception { - InputStream is = null; - rs.setBlob(1, is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBlob(1, is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0108(RowSet rs) throws Exception { - InputStream is = null; - rs.setBlob("one", is); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBlob("one", is); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0109(RowSet rs) throws Exception { - InputStream is = null; - rs.setBlob("one", is, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + InputStream is = null; + rs.setBlob("one", is, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0110(RowSet rs) throws Exception { - rs.setBlob("one", new StubBlob()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setBlob("one", new StubBlob())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0111(RowSet rs) throws Exception { - rs.setBoolean("one", true); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setBoolean("one", true)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0112(RowSet rs) throws Exception { - byte b = 1; - rs.setByte("one", b); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + byte b = 1; + rs.setByte("one", b); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0113(RowSet rs) throws Exception { - byte b = 1; - rs.setBytes("one", new byte[10]); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + byte b = 1; + rs.setBytes("one", new byte[10]); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0114(RowSet rs) throws Exception { - Reader rdr = null; - rs.setCharacterStream("one", rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setCharacterStream("one", rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0115(RowSet rs) throws Exception { - Reader rdr = null; - rs.setCharacterStream("one", rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setCharacterStream("one", rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0116(RowSet rs) throws Exception { - Reader rdr = null; - rs.setCharacterStream(1, rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setCharacterStream(1, rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0117(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob(1, rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob(1, rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0118(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob("one", rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob("one", rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0119(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob("one", rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob("one", rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0120(RowSet rs) throws Exception { - rs.setClob("one", new StubClob()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setClob("one", new StubClob())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0121(RowSet rs) throws Exception { - rs.setDate("one", Date.valueOf(LocalDate.now())); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setDate("one", Date.valueOf(LocalDate.now()))); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0122(RowSet rs) throws Exception { - rs.setDate("one", Date.valueOf(LocalDate.now()), - Calendar.getInstance()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setDate("one", Date.valueOf(LocalDate.now()), + Calendar.getInstance())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0123(RowSet rs) throws Exception { - rs.setTime("one", Time.valueOf(LocalTime.now())); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setTime("one", Time.valueOf(LocalTime.now()))); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0124(RowSet rs) throws Exception { - rs.setTime("one", Time.valueOf(LocalTime.now()), - Calendar.getInstance()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setTime("one", Time.valueOf(LocalTime.now()), + Calendar.getInstance())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0125(RowSet rs) throws Exception { - rs.setTimestamp("one", Timestamp.valueOf(LocalDateTime.now())); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setTimestamp("one", Timestamp.valueOf(LocalDateTime.now()))); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0126(RowSet rs) throws Exception { - rs.setTimestamp("one", Timestamp.valueOf(LocalDateTime.now()), - Calendar.getInstance()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setTimestamp("one", Timestamp.valueOf(LocalDateTime.now()), + Calendar.getInstance())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0127(RowSet rs) throws Exception { - rs.setDouble("one", 2.0d); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setDouble("one", 2.0d)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0128(RowSet rs) throws Exception { - rs.setFloat("one", 2.0f); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setFloat("one", 2.0f)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0129(RowSet rs) throws Exception { - rs.setInt("one", 21); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setInt("one", 21)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0130(RowSet rs) throws Exception { - rs.setLong("one", 21l); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setLong("one", 21l)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0131(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNCharacterStream("one", rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNCharacterStream("one", rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0132(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNCharacterStream("one", rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNCharacterStream("one", rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0133(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNCharacterStream(1, rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNCharacterStream(1, rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0134(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNCharacterStream(1, rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNCharacterStream(1, rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0135(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob("one", rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob("one", rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0136(RowSet rs) throws Exception { - Reader rdr = null; - rs.setClob("one", rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setClob("one", rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0137(RowSet rs) throws Exception { - rs.setNClob("one", new StubNClob()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNClob("one", new StubNClob())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0138(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNClob(1, rdr); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNClob(1, rdr); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0139(RowSet rs) throws Exception { - Reader rdr = null; - rs.setNClob(1, rdr, query.length()); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + Reader rdr = null; + rs.setNClob(1, rdr, query.length()); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0140(RowSet rs) throws Exception { - rs.setNClob(1, new StubNClob()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNClob(1, new StubNClob())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0141(RowSet rs) throws Exception { - rs.setNString(1, query); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNString(1, query)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0142(RowSet rs) throws Exception { - rs.setNull("one", Types.INTEGER); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNull("one", Types.INTEGER)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0143(RowSet rs) throws Exception { - rs.setNull("one", Types.INTEGER, "my.type"); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNull("one", Types.INTEGER, "my.type")); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0144(RowSet rs) throws Exception { - rs.setObject("one", query, Types.VARCHAR); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setObject("one", query, Types.VARCHAR)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0145(RowSet rs) throws Exception { - rs.setObject("one", query, Types.VARCHAR, 0); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setObject("one", query, Types.VARCHAR, 0)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0146(RowSet rs) throws Exception { - rs.setObject("one", query); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setObject("one", query)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0147(RowSet rs) throws Exception { - RowId aRowid = null; - rs.setRowId("one", aRowid); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + RowId aRowid = null; + rs.setRowId("one", aRowid); + }); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0148(RowSet rs) throws Exception { - rs.setSQLXML("one", new StubSQLXML()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setSQLXML("one", new StubSQLXML())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0149(RowSet rs) throws Exception { - rs.setSQLXML(1, new StubSQLXML()); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setSQLXML(1, new StubSQLXML())); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0150(RowSet rs) throws Exception { - rs.setNString(1, query); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNString(1, query)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0151(RowSet rs) throws Exception { - rs.setNString("one", query); + assertThrows(SQLFeatureNotSupportedException.class, () -> rs.setNString("one", query)); } /* * This method is currently not implemented in BaseRowSet and will * throw a SQLFeatureNotSupportedException */ - @Test(dataProvider = "rowSetType", - expectedExceptions = SQLFeatureNotSupportedException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonRowSetTest0152(RowSet rs) throws Exception { - short val = 21; - rs.setShort("one", val); + assertThrows(SQLFeatureNotSupportedException.class, () -> { + short val = 21; + rs.setShort("one", val); + }); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/RowSetFactoryTests.java b/test/jdk/javax/sql/test/rowset/RowSetFactoryTests.java similarity index 80% rename from test/jdk/javax/sql/testng/test/rowset/RowSetFactoryTests.java rename to test/jdk/javax/sql/test/rowset/RowSetFactoryTests.java index 72a4341c7aa..480033afc2b 100644 --- a/test/jdk/javax/sql/testng/test/rowset/RowSetFactoryTests.java +++ b/test/jdk/javax/sql/test/rowset/RowSetFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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,11 +23,15 @@ package test.rowset; import java.sql.SQLException; +import java.util.stream.Stream; import javax.sql.rowset.RowSetFactory; import javax.sql.rowset.RowSetProvider; -import static org.testng.Assert.*; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; public class RowSetFactoryTests extends BaseTest { @@ -50,7 +54,8 @@ public class RowSetFactoryTests extends BaseTest { * Validate that the RowSetFactory returned by RowSetProvider.newFactory() * returns the correct RowSet implementations */ - @Test(dataProvider = "RowSetValues", enabled = true) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("RowSetValues") public void test(RowSetFactory rsf, String impl) throws SQLException { validateRowSetImpl(rsf, impl); } @@ -98,22 +103,20 @@ public class RowSetFactoryTests extends BaseTest { * DataProvider used to provide the RowSetFactory and the RowSet * implementation that should be returned */ - @DataProvider(name = "RowSetValues") - private Object[][] RowSetValues() throws SQLException { + private Stream RowSetValues() throws SQLException { RowSetFactory rsf = RowSetProvider.newFactory(); RowSetFactory rsf1 = RowSetProvider.newFactory(STUB_FACTORY_CLASSNAME, null); - return new Object[][]{ - {rsf, DEFAULT_CACHEDROWSET_CLASSNAME}, - {rsf, DEFAULT_FILTEREDROWSET_CLASSNAME}, - {rsf, DEFAULT_JDBCROWSET_CLASSNAME}, - {rsf, DEFAULT_JOINROWSET_CLASSNAME}, - {rsf, DEFAULT_WEBROWSET_CLASSNAME}, - {rsf1, STUB_CACHEDROWSET_CLASSNAME}, - {rsf1, STUB_FILTEREDROWSET_CLASSNAME}, - {rsf1, STUB_JDBCROWSET_CLASSNAME}, - {rsf1, STUB_JOINROWSET_CLASSNAME}, - {rsf1, STUB_WEBROWSET_CLASSNAME} - - }; + return Stream.of( + Arguments.of(rsf, DEFAULT_CACHEDROWSET_CLASSNAME), + Arguments.of(rsf, DEFAULT_FILTEREDROWSET_CLASSNAME), + Arguments.of(rsf, DEFAULT_JDBCROWSET_CLASSNAME), + Arguments.of(rsf, DEFAULT_JOINROWSET_CLASSNAME), + Arguments.of(rsf, DEFAULT_WEBROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_CACHEDROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_FILTEREDROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_JDBCROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_JOINROWSET_CLASSNAME), + Arguments.of(rsf1, STUB_WEBROWSET_CLASSNAME) + ); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/RowSetMetaDataTests.java b/test/jdk/javax/sql/test/rowset/RowSetMetaDataTests.java similarity index 55% rename from test/jdk/javax/sql/testng/test/rowset/RowSetMetaDataTests.java rename to test/jdk/javax/sql/test/rowset/RowSetMetaDataTests.java index 8a944a8bbb1..59dc4f43033 100644 --- a/test/jdk/javax/sql/testng/test/rowset/RowSetMetaDataTests.java +++ b/test/jdk/javax/sql/test/rowset/RowSetMetaDataTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -25,12 +25,19 @@ package test.rowset; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; +import java.util.stream.IntStream; +import java.util.stream.Stream; import javax.sql.RowSetMetaData; import javax.sql.rowset.RowSetMetaDataImpl; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import org.junit.jupiter.params.provider.ValueSource; import util.BaseTest; public class RowSetMetaDataTests extends BaseTest { @@ -40,7 +47,7 @@ public class RowSetMetaDataTests extends BaseTest { // Instance to be used within the tests private RowSetMetaDataImpl rsmd; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { rsmd = new RowSetMetaDataImpl(); rsmd.setColumnCount(MAX_COLUMNS); @@ -49,325 +56,325 @@ public class RowSetMetaDataTests extends BaseTest { /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test(Integer col) throws Exception { - rsmd.getCatalogName(col); + assertThrows(SQLException.class, () -> rsmd.getCatalogName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test01(Integer col) throws Exception { - rsmd.getColumnClassName(col); + assertThrows(SQLException.class, () -> rsmd.getColumnClassName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test02(Integer col) throws Exception { - rsmd.getColumnDisplaySize(col); + assertThrows(SQLException.class, () -> rsmd.getColumnDisplaySize(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test03(Integer col) throws Exception { - rsmd.getColumnLabel(col); + assertThrows(SQLException.class, () -> rsmd.getColumnLabel(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test04(Integer col) throws Exception { - rsmd.getColumnName(col); + assertThrows(SQLException.class, () -> rsmd.getColumnName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test05(Integer col) throws Exception { - rsmd.getColumnType(col); + assertThrows(SQLException.class, () -> rsmd.getColumnType(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test06(Integer col) throws Exception { - rsmd.getColumnTypeName(col); + assertThrows(SQLException.class, () -> rsmd.getColumnTypeName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test07(Integer col) throws Exception { - rsmd.getPrecision(col); + assertThrows(SQLException.class, () -> rsmd.getPrecision(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test08(Integer col) throws Exception { - rsmd.getScale(col); + assertThrows(SQLException.class, () -> rsmd.getScale(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test09(Integer col) throws Exception { - rsmd.getSchemaName(col); + assertThrows(SQLException.class, () -> rsmd.getSchemaName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test10(Integer col) throws Exception { - rsmd.getTableName(col); + assertThrows(SQLException.class, () -> rsmd.getTableName(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test11(Integer col) throws Exception { - rsmd.isAutoIncrement(col); + assertThrows(SQLException.class, () -> rsmd.isAutoIncrement(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test12(Integer col) throws Exception { - rsmd.isCaseSensitive(col); + assertThrows(SQLException.class, () -> rsmd.isCaseSensitive(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test13(Integer col) throws Exception { - rsmd.isCurrency(col); + assertThrows(SQLException.class, () -> rsmd.isCurrency(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test14(Integer col) throws Exception { - rsmd.isDefinitelyWritable(col); + assertThrows(SQLException.class, () -> rsmd.isDefinitelyWritable(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test15(Integer col) throws Exception { - rsmd.isNullable(col); + assertThrows(SQLException.class, () -> rsmd.isNullable(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test16(Integer col) throws Exception { - rsmd.isReadOnly(col); + assertThrows(SQLException.class, () -> rsmd.isReadOnly(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test17(Integer col) throws Exception { - rsmd.isSearchable(col); + assertThrows(SQLException.class, () -> rsmd.isSearchable(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test18(Integer col) throws Exception { - rsmd.isSigned(col); + assertThrows(SQLException.class, () -> rsmd.isSigned(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test19(Integer col) throws Exception { - rsmd.isWritable(col); + assertThrows(SQLException.class, () -> rsmd.isWritable(col)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test20(Integer col) throws Exception { - rsmd.setAutoIncrement(col, true); + assertThrows(SQLException.class, () -> rsmd.setAutoIncrement(col, true)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test21(Integer col) throws Exception { - rsmd.setCaseSensitive(col, true); + assertThrows(SQLException.class, () -> rsmd.setCaseSensitive(col, true)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test22(Integer col) throws Exception { - rsmd.setCatalogName(col, null); + assertThrows(SQLException.class, () -> rsmd.setCatalogName(col, null)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test23(Integer col) throws Exception { - rsmd.setColumnDisplaySize(col, 5); + assertThrows(SQLException.class, () -> rsmd.setColumnDisplaySize(col, 5)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test24(Integer col) throws Exception { - rsmd.setColumnLabel(col, "label"); + assertThrows(SQLException.class, () -> rsmd.setColumnLabel(col, "label")); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test25(Integer col) throws Exception { - rsmd.setColumnName(col, "F1"); + assertThrows(SQLException.class, () -> rsmd.setColumnName(col, "F1")); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test26(Integer col) throws Exception { - rsmd.setColumnType(col, Types.CHAR); + assertThrows(SQLException.class, () -> rsmd.setColumnType(col, Types.CHAR)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test27(Integer col) throws Exception { - rsmd.setColumnTypeName(col, "F1"); + assertThrows(SQLException.class, () -> rsmd.setColumnTypeName(col, "F1")); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test28(Integer col) throws Exception { - rsmd.setCurrency(col, true); + assertThrows(SQLException.class, () -> rsmd.setCurrency(col, true)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test29(Integer col) throws Exception { - rsmd.setNullable(col, ResultSetMetaData.columnNoNulls); + assertThrows(SQLException.class, () -> rsmd.setNullable(col, ResultSetMetaData.columnNoNulls)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test30(Integer col) throws Exception { - rsmd.setPrecision(col, 2); + assertThrows(SQLException.class, () -> rsmd.setPrecision(col, 2)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test31(Integer col) throws Exception { - rsmd.setScale(col, 2); + assertThrows(SQLException.class, () -> rsmd.setScale(col, 2)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test32(Integer col) throws Exception { - rsmd.setSchemaName(col, "Gotham"); + assertThrows(SQLException.class, () -> rsmd.setSchemaName(col, "Gotham")); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test33(Integer col) throws Exception { - rsmd.setSearchable(col, false); + assertThrows(SQLException.class, () -> rsmd.setSearchable(col, false)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test34(Integer col) throws Exception { - rsmd.setSigned(col, false); + assertThrows(SQLException.class, () -> rsmd.setSigned(col, false)); } /* * Validate a SQLException is thrown for an invalid column index */ - @Test(dataProvider = "invalidColumnRanges", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("invalidColumnRanges") public void test35(Integer col) throws Exception { - rsmd.setTableName(col, "SUPERHEROS"); + assertThrows(SQLException.class, () -> rsmd.setTableName(col, "SUPERHEROS")); } /* @@ -375,7 +382,8 @@ public class RowSetMetaDataTests extends BaseTest { * Note: Once setColumnClassName is added to RowSetMetaData, this * method will need to change. */ - @Test(dataProvider = "columnClassNames") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("columnClassNames") public void test36(Integer type, String name) throws Exception { rsmd.setColumnType(1, type); assertTrue(rsmd.getColumnClassName(1).equals(name)); @@ -385,7 +393,8 @@ public class RowSetMetaDataTests extends BaseTest { * Validate that all of the methods are accessible and the correct value * is returned for each column */ - @Test(dataProvider = "columnRanges") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("columnRanges") public void test37(Integer col) throws Exception { rsmd.setAutoIncrement(col, true); assertTrue(rsmd.isAutoIncrement(col)); @@ -429,7 +438,8 @@ public class RowSetMetaDataTests extends BaseTest { /* * Validate that the proper values are accepted by setNullable */ - @Test(dataProvider = "validSetNullableValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("validSetNullableValues") public void test38(Integer val) throws Exception { rsmd.setNullable(1, val); } @@ -437,7 +447,8 @@ public class RowSetMetaDataTests extends BaseTest { /* * Validate that the correct type is returned for the column */ - @Test(dataProvider = "jdbcTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("jdbcTypes") public void test39(Integer type) throws Exception { rsmd.setColumnType(1, type); assertTrue(type == rsmd.getColumnType(1)); @@ -446,7 +457,8 @@ public class RowSetMetaDataTests extends BaseTest { /* * Validate that the correct value is returned from the isXXX methods */ - @Test(dataProvider = "trueFalse") + @ParameterizedTest(autoCloseArguments = false) + @ValueSource(booleans = {true, false}) public void test40(Boolean b) throws Exception { rsmd.setAutoIncrement(1, b); rsmd.setCaseSensitive(1, b); @@ -483,73 +495,62 @@ public class RowSetMetaDataTests extends BaseTest { * to validate that an IllegalArgumentException will be thrown from the * valueOf method */ - @DataProvider(name = "validSetNullableValues") - private Object[][] validSetNullableValues() { - return new Object[][]{ - {ResultSetMetaData.columnNoNulls}, - {ResultSetMetaData.columnNullable}, - {ResultSetMetaData.columnNullableUnknown} - }; + private Stream validSetNullableValues() { + return Stream.of( + ResultSetMetaData.columnNoNulls, + ResultSetMetaData.columnNullable, + ResultSetMetaData.columnNullableUnknown + ); } /* * DataProvider used to provide column indexes that are out of range so that * SQLException is thrown */ - @DataProvider(name = "invalidColumnRanges") - private Object[][] invalidColumnRanges() { - return new Object[][]{ - {-1}, - {0}, - {MAX_COLUMNS + 1} - }; + private Stream invalidColumnRanges() { + return Stream.of( + -1, + 0, + MAX_COLUMNS + 1 + ); } /* * DataProvider used to provide the valid column ranges for the * RowSetMetaDataImpl object */ - @DataProvider(name = "columnRanges") - private Object[][] columnRanges() { - Object[][] o = new Object[MAX_COLUMNS][1]; - for (int i = 1; i <= MAX_COLUMNS; i++) { - o[i - 1][0] = i; - } - return o; + private Stream columnRanges() { + return IntStream.rangeClosed(1, MAX_COLUMNS).boxed(); } /* * DataProvider used to specify the value to set via setColumnType and * the expected value to be returned from getColumnClassName */ - @DataProvider(name = "columnClassNames") - private Object[][] columnClassNames() { - return new Object[][]{ - {Types.CHAR, "java.lang.String"}, - {Types.NCHAR, "java.lang.String"}, - {Types.VARCHAR, "java.lang.String"}, - {Types.NVARCHAR, "java.lang.String"}, - {Types.LONGVARCHAR, "java.lang.String"}, - {Types.LONGNVARCHAR, "java.lang.String"}, - {Types.NUMERIC, "java.math.BigDecimal"}, - {Types.DECIMAL, "java.math.BigDecimal"}, - {Types.BIT, "java.lang.Boolean"}, - {Types.TINYINT, "java.lang.Byte"}, - {Types.SMALLINT, "java.lang.Short"}, - {Types.INTEGER, "java.lang.Integer"}, - {Types.FLOAT, "java.lang.Double"}, - {Types.DOUBLE, "java.lang.Double"}, - {Types.BINARY, "byte[]"}, - {Types.VARBINARY, "byte[]"}, - {Types.LONGVARBINARY, "byte[]"}, - {Types.DATE, "java.sql.Date"}, - {Types.TIME, "java.sql.Time"}, - {Types.TIMESTAMP, "java.sql.Timestamp"}, - {Types.CLOB, "java.sql.Clob"}, - {Types.BLOB, "java.sql.Blob"} - - }; - + private Stream columnClassNames() { + return Stream.of( + Arguments.of(Types.CHAR, "java.lang.String"), + Arguments.of(Types.NCHAR, "java.lang.String"), + Arguments.of(Types.VARCHAR, "java.lang.String"), + Arguments.of(Types.NVARCHAR, "java.lang.String"), + Arguments.of(Types.LONGVARCHAR, "java.lang.String"), + Arguments.of(Types.LONGNVARCHAR, "java.lang.String"), + Arguments.of(Types.NUMERIC, "java.math.BigDecimal"), + Arguments.of(Types.DECIMAL, "java.math.BigDecimal"), + Arguments.of(Types.BIT, "java.lang.Boolean"), + Arguments.of(Types.TINYINT, "java.lang.Byte"), + Arguments.of(Types.SMALLINT, "java.lang.Short"), + Arguments.of(Types.INTEGER, "java.lang.Integer"), + Arguments.of(Types.FLOAT, "java.lang.Double"), + Arguments.of(Types.DOUBLE, "java.lang.Double"), + Arguments.of(Types.BINARY, "byte[]"), + Arguments.of(Types.VARBINARY, "byte[]"), + Arguments.of(Types.LONGVARBINARY, "byte[]"), + Arguments.of(Types.DATE, "java.sql.Date"), + Arguments.of(Types.TIME, "java.sql.Time"), + Arguments.of(Types.TIMESTAMP, "java.sql.Timestamp"), + Arguments.of(Types.CLOB, "java.sql.Clob"), + Arguments.of(Types.BLOB, "java.sql.Blob") + ); } - } diff --git a/test/jdk/javax/sql/testng/test/rowset/RowSetProviderTests.java b/test/jdk/javax/sql/test/rowset/RowSetProviderTests.java similarity index 79% rename from test/jdk/javax/sql/testng/test/rowset/RowSetProviderTests.java rename to test/jdk/javax/sql/test/rowset/RowSetProviderTests.java index 47a5cdb1eb0..25422cf0950 100644 --- a/test/jdk/javax/sql/testng/test/rowset/RowSetProviderTests.java +++ b/test/jdk/javax/sql/test/rowset/RowSetProviderTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -27,14 +27,19 @@ import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.sql.SQLException; +import java.util.stream.Stream; import javax.sql.rowset.RowSetFactory; import javax.sql.rowset.RowSetProvider; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import util.BaseTest; import util.StubRowSetFactory; @@ -57,7 +62,7 @@ public class RowSetProviderTests extends BaseTest { * Save off the original property value for javax.sql.rowset.RowSetFactory, * original classloader and define the path to the jars directory */ - @BeforeClass + @BeforeAll public static void setUpClass() throws Exception { origFactoryProperty = System.getProperty("javax.sql.rowset.RowSetFactory"); cl = Thread.currentThread().getContextClassLoader(); @@ -68,7 +73,7 @@ public class RowSetProviderTests extends BaseTest { /* * Install the original javax.sql.rowset.RowSetFactory property value */ - @AfterClass + @AfterAll public static void tearDownClass() throws Exception { if (origFactoryProperty != null) { System.setProperty("javax.sql.rowset.RowSetFactory", @@ -80,7 +85,7 @@ public class RowSetProviderTests extends BaseTest { * Clear the javax.sql.rowset.RowSetFactory property value and * reset the classloader to its original value */ - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { System.clearProperty("javax.sql.rowset.RowSetFactory"); Thread.currentThread().setContextClassLoader(cl); @@ -89,7 +94,8 @@ public class RowSetProviderTests extends BaseTest { /* * Validate that the correct RowSetFactory is returned by newFactory(). */ - @Test(dataProvider = "RowSetFactoryValues") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("RowSetFactoryValues") public void test(RowSetFactory rsf, String impl) throws SQLException { validateProvider(rsf, impl); } @@ -109,7 +115,7 @@ public class RowSetProviderTests extends BaseTest { * Validate that the correct RowSetFactory is returned by newFactory() * when specified by the javax.sql.rowset.RowSetFactory property. */ - @Test(enabled = true) + @Test public void test02() throws SQLException { System.setProperty("javax.sql.rowset.RowSetFactory", STUB_FACTORY_CLASSNAME); validateProvider(RowSetProvider.newFactory(), STUB_FACTORY_CLASSNAME); @@ -120,11 +126,13 @@ public class RowSetProviderTests extends BaseTest { * when specified RowSetFactory specified by the * javax.sql.rowset.RowSetFactory property is not valid. */ - @Test(expectedExceptions = SQLException.class) + @Test public void test03() throws SQLException { - System.setProperty("javax.sql.rowset.RowSetFactory", - "invalid.RowSetFactoryImpl"); - RowSetFactory rsf = RowSetProvider.newFactory(); + assertThrows(SQLException.class, () -> { + System.setProperty("javax.sql.rowset.RowSetFactory", + "invalid.RowSetFactoryImpl"); + RowSetFactory rsf = RowSetProvider.newFactory(); + }); } /* @@ -144,13 +152,15 @@ public class RowSetProviderTests extends BaseTest { * Validate that a SQLException is thrown by newFactory() if the default * RowSetFactory specified by the ServiceLoader API is not valid */ - @Test(expectedExceptions = SQLException.class) + @Test public void test05() throws Exception { - File f = new File(jarPath + "badFactory"); - URLClassLoader loader = new URLClassLoader(new URL[]{ - new URL(f.toURI().toString())}, getClass().getClassLoader()); - Thread.currentThread().setContextClassLoader(loader); - RowSetProvider.newFactory(); + assertThrows(SQLException.class, () -> { + File f = new File(jarPath + "badFactory"); + URLClassLoader loader = new URLClassLoader(new URL[]{ + new URL(f.toURI().toString())}, getClass().getClassLoader()); + Thread.currentThread().setContextClassLoader(loader); + RowSetProvider.newFactory(); + }); } /* @@ -174,16 +184,15 @@ public class RowSetProviderTests extends BaseTest { * DataProvider used to provide a RowSetFactory and the expected * RowSetFactory implementation that should be returned */ - @DataProvider(name = "RowSetFactoryValues") - private Object[][] RowSetFactoryValues() throws SQLException { + private Stream RowSetFactoryValues() throws SQLException { RowSetFactory rsf = RowSetProvider.newFactory(); RowSetFactory rsf1 = RowSetProvider.newFactory(STUB_FACTORY_CLASSNAME, null); RowSetFactory rsf2 = RowSetProvider.newFactory(DEFFAULT_FACTORY_CLASSNAME, null); - return new Object[][]{ - {rsf, NO_VALADATE_IMPL}, - {rsf, DEFFAULT_FACTORY_CLASSNAME}, - {rsf1, STUB_FACTORY_CLASSNAME}, - {rsf2, DEFFAULT_FACTORY_CLASSNAME} - }; + return Stream.of( + Arguments.of(rsf, NO_VALADATE_IMPL), + Arguments.of(rsf, DEFFAULT_FACTORY_CLASSNAME), + Arguments.of(rsf1, STUB_FACTORY_CLASSNAME), + Arguments.of(rsf2, DEFFAULT_FACTORY_CLASSNAME) + ); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/RowSetWarningTests.java b/test/jdk/javax/sql/test/rowset/RowSetWarningTests.java similarity index 97% rename from test/jdk/javax/sql/testng/test/rowset/RowSetWarningTests.java rename to test/jdk/javax/sql/test/rowset/RowSetWarningTests.java index 41b52c4ef57..81ae97af785 100644 --- a/test/jdk/javax/sql/testng/test/rowset/RowSetWarningTests.java +++ b/test/jdk/javax/sql/test/rowset/RowSetWarningTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -24,8 +24,10 @@ package test.rowset; import java.sql.SQLException; import javax.sql.rowset.RowSetWarning; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class RowSetWarningTests extends BaseTest { diff --git a/test/jdk/javax/sql/testng/test/rowset/cachedrowset/CachedRowSetTests.java b/test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetTests.java similarity index 94% rename from test/jdk/javax/sql/testng/test/rowset/cachedrowset/CachedRowSetTests.java rename to test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetTests.java index 1155a889187..5fd473924c3 100644 --- a/test/jdk/javax/sql/testng/test/rowset/cachedrowset/CachedRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/cachedrowset/CachedRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 diff --git a/test/jdk/javax/sql/testng/test/rowset/cachedrowset/CommonCachedRowSetTests.java b/test/jdk/javax/sql/test/rowset/cachedrowset/CommonCachedRowSetTests.java similarity index 79% rename from test/jdk/javax/sql/testng/test/rowset/cachedrowset/CommonCachedRowSetTests.java rename to test/jdk/javax/sql/test/rowset/cachedrowset/CommonCachedRowSetTests.java index 642c03c9a45..bc63ce28497 100644 --- a/test/jdk/javax/sql/testng/test/rowset/cachedrowset/CommonCachedRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/cachedrowset/CommonCachedRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 @@ -37,6 +37,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Collection; +import java.util.stream.Stream; import javax.sql.RowSet; import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.RowSetMetaDataImpl; @@ -44,12 +45,18 @@ import javax.sql.rowset.serial.SerialRef; import javax.sql.rowset.spi.SyncFactory; import javax.sql.rowset.spi.SyncProvider; import javax.sql.rowset.spi.SyncProviderException; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import test.rowset.CommonRowSetTests; import util.StubArray; import util.StubRef; @@ -83,52 +90,43 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * DataProvider that uses a RowSet with the COFFEE_HOUSES Table */ - @DataProvider(name = "rowsetUsingCoffeeHouses") - protected Object[][] rowsetUsingCoffeeHouses() throws Exception { - RowSet rs = createCoffeeHousesRowSet(); - return new Object[][]{ - {rs} - }; + protected Stream rowsetUsingCoffeeHouses() throws Exception { + return Stream.of(createCoffeeHousesRowSet()); } /* * DataProvider that uses a RowSet with the COFFEES Table */ - @DataProvider(name = "rowsetUsingCoffees") - protected Object[][] rowsetUsingCoffees() throws Exception { - RowSet rs = createCoffeesRowSet(); - return new Object[][]{ - {rs} - }; + protected Stream rowsetUsingCoffees() throws Exception { + return Stream.of(createCoffeesRowSet()); } /* * DataProvider that uses a RowSet with the DATAYPES Table and * used to validate the various supported data types */ - @DataProvider(name = "rowsetUsingDataTypes") - protected Object[][] rowsetUsingDataTypes() throws Exception { + protected Stream rowsetUsingDataTypes() throws Exception { CachedRowSet rs = createDataTypesRowSet(); - return new Object[][]{ - {rs, JDBCType.INTEGER}, - {rs, JDBCType.CHAR}, - {rs, JDBCType.VARCHAR}, - {rs, JDBCType.BIGINT}, - {rs, JDBCType.BOOLEAN}, - {rs, JDBCType.SMALLINT}, - {rs, JDBCType.DOUBLE}, - {rs, JDBCType.DECIMAL}, - {rs, JDBCType.REAL}, - {rs, JDBCType.TINYINT}, - {rs, JDBCType.DATE}, - {rs, JDBCType.TIME}, - {rs, JDBCType.TIMESTAMP}, - {rs, JDBCType.VARBINARY}, - {rs, JDBCType.ARRAY}, - {rs, JDBCType.REF}, - {rs, JDBCType.FLOAT} - }; + return Stream.of( + Arguments.of(rs, JDBCType.INTEGER), + Arguments.of(rs, JDBCType.CHAR), + Arguments.of(rs, JDBCType.VARCHAR), + Arguments.of(rs, JDBCType.BIGINT), + Arguments.of(rs, JDBCType.BOOLEAN), + Arguments.of(rs, JDBCType.SMALLINT), + Arguments.of(rs, JDBCType.DOUBLE), + Arguments.of(rs, JDBCType.DECIMAL), + Arguments.of(rs, JDBCType.REAL), + Arguments.of(rs, JDBCType.TINYINT), + Arguments.of(rs, JDBCType.DATE), + Arguments.of(rs, JDBCType.TIME), + Arguments.of(rs, JDBCType.TIMESTAMP), + Arguments.of(rs, JDBCType.VARBINARY), + Arguments.of(rs, JDBCType.ARRAY), + Arguments.of(rs, JDBCType.REF), + Arguments.of(rs, JDBCType.FLOAT) + ); } /* @@ -254,7 +252,7 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { private void compareMetaData(ResultSetMetaData rsmd, ResultSetMetaData rsmd1) throws SQLException { - assertEquals(rsmd1.getColumnCount(), rsmd.getColumnCount()); + assertEquals(rsmd.getColumnCount(), rsmd1.getColumnCount()); int cols = rsmd.getColumnCount(); for (int i = 1; i <= cols; i++) { assertTrue(rsmd1.getCatalogName(i).equals(rsmd.getCatalogName(i))); @@ -356,26 +354,33 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate SyncProviderException is thrown when acceptChanges is called * but there is not a way to make a connection to the datasource */ - @Test(dataProvider = "rowSetType", expectedExceptions = SyncProviderException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0000(CachedRowSet rs) throws Exception { - rs.acceptChanges(); - rs.close(); + assertThrows(SyncProviderException.class, () -> { + rs.acceptChanges(); + rs.close(); + }); } /* * Validate SyncProviderException is thrown when acceptChanges is called * when null is passed as the datasource */ - @Test(dataProvider = "rowSetType", expectedExceptions = SyncProviderException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0001(CachedRowSet rs) throws Exception { - rs.acceptChanges(null); - rs.close(); + assertThrows(SyncProviderException.class, () -> { + rs.acceptChanges(null); + rs.close(); + }); } /* * Validate that that RIOPtimsticProvider is the default SyncProvider */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0002(CachedRowSet rs) throws SQLException { SyncProvider sp = rs.getSyncProvider(); assertTrue(sp instanceof com.sun.rowset.providers.RIOptimisticProvider); @@ -385,7 +390,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that you can specify a SyncProvider */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0003(CachedRowSet rs) throws SQLException { // Register a provider and make sure it is avaiable @@ -400,7 +406,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowSetChanged is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0004(CachedRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -412,7 +419,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowSetChanged is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0005(CachedRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -424,7 +432,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyRowChanged is called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0006(RowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -443,7 +452,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Create a multiple RowSetListeners and validate that notifyRowChanged, * notifiyMoved is called on all listners */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0007(RowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); TestRowSetListener rsl2 = new TestRowSetListener(); @@ -467,7 +477,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Create a RowSetListener and validate that notifyRowChanged and * notifyCursorMoved are called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0008(CachedRowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -487,7 +498,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Create a RowSetListener and validate that notifyCursorMoved is called */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0009(RowSet rs) throws Exception { TestRowSetListener rsl = new TestRowSetListener(); rs.addRowSetListener(rsl); @@ -499,7 +511,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that getTableName() returns the proper values */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0010(CachedRowSet rs) throws Exception { assertNull(rs.getTableName()); rs.setTableName(COFFEE_HOUSES_TABLE); @@ -510,12 +523,13 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that getKeyColumns() returns the proper values */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0011(CachedRowSet rs) throws Exception { int[] pkeys = {1, 3}; assertNull(rs.getKeyColumns()); rs.setKeyColumns(pkeys); - assertEquals(rs.getKeyColumns(), pkeys); + assertArrayEquals(pkeys, rs.getKeyColumns()); rs.close(); } @@ -523,52 +537,62 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that setMatchColumn throws a SQLException if the column * index specified is out of range */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0012(CachedRowSet rs) throws Exception { - rs.setMatchColumn(-1); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setMatchColumn(-1); + rs.close(); + }); } /* * Validate that setMatchColumn throws a SQLException if the column * index specified is out of range */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0013(CachedRowSet rs) throws Exception { - int[] cols = {1, -1}; - rs.setMatchColumn(cols); - rs.close(); + assertThrows(SQLException.class, () -> { + int[] cols = {1, -1}; + rs.setMatchColumn(cols); + rs.close(); + }); } /* * Validate that setMatchColumn throws a SQLException if the column * index specified is out of range */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0014(CachedRowSet rs) throws Exception { - rs.setMatchColumn((String) null); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setMatchColumn((String) null); + rs.close(); + }); } /* * Validate that setMatchColumn throws a SQLException if the column * index specified is out of range */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0015(CachedRowSet rs) throws Exception { - String[] cols = {"ID", null}; - rs.setMatchColumn(cols); + assertThrows(SQLException.class, () -> { + String[] cols = {"ID", null}; + rs.setMatchColumn(cols); + }); } /* * Validate that getMatchColumn returns the same value specified by * setMatchColumn */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0016(CachedRowSet rs) throws Exception { int[] expectedCols = {1}; String[] expectedColNames = {"ID"}; @@ -578,8 +602,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { for (int i = 0; i < actualCols.length; i++) { System.out.println(actualCols[i]); } - assertEquals(actualCols, expectedCols); - assertEquals(actualColNames, expectedColNames); + assertArrayEquals(expectedCols, actualCols); + assertArrayEquals(expectedColNames, actualColNames); rs.close(); } @@ -587,15 +611,17 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that getMatchColumn returns the same value specified by * setMatchColumn */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0017(CachedRowSet rs) throws Exception { int[] expectedCols = {1}; String[] expectedColNames = {"ID"}; rs.setMatchColumn(expectedColNames[0]); int[] actualCols = rs.getMatchColumnIndexes(); String[] actualColNames = rs.getMatchColumnNames(); - assertEquals(actualCols, expectedCols); - assertEquals(actualColNames, expectedColNames); + assertArrayEquals(expectedCols, actualCols); + assertArrayEquals(expectedColNames, actualColNames); rs.close(); } @@ -603,16 +629,18 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that getMatchColumn returns the same valid value specified by * setMatchColumn */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0018(CachedRowSet rs) throws Exception { int[] expectedCols = {1, 3}; String[] expectedColNames = {"COF_ID", "SUP_ID"}; rs.setMatchColumn(expectedCols); int[] actualCols = rs.getMatchColumnIndexes(); String[] actualColNames = rs.getMatchColumnNames(); - assertEquals(actualCols, expectedCols); - assertEquals(actualColNames, expectedColNames); - assertEquals(actualCols, expectedCols); + assertArrayEquals(expectedCols, actualCols); + assertArrayEquals(expectedColNames, actualColNames); + assertArrayEquals(expectedCols, actualCols); rs.close(); } @@ -620,15 +648,17 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that getMatchColumn returns the same valid value specified by * setMatchColumn */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0019(CachedRowSet rs) throws Exception { int[] expectedCols = {1, 3}; String[] expectedColNames = {"COF_ID", "SUP_ID"}; rs.setMatchColumn(expectedColNames); int[] actualCols = rs.getMatchColumnIndexes(); String[] actualColNames = rs.getMatchColumnNames(); - assertEquals(actualCols, expectedCols); - assertEquals(actualColNames, expectedColNames); + assertArrayEquals(expectedCols, actualCols); + assertArrayEquals(expectedColNames, actualColNames); rs.close(); } @@ -636,69 +666,78 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that getMatchColumnIndexes throws a SQLException if * unsetMatchColumn has been called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0020(CachedRowSet rs) throws Exception { - rs.setMatchColumn(1); - int[] actualCols = rs.getMatchColumnIndexes(); - assertTrue(actualCols != null); - rs.unsetMatchColumn(1); - actualCols = rs.getMatchColumnIndexes(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setMatchColumn(1); + int[] actualCols = rs.getMatchColumnIndexes(); + assertTrue(actualCols != null); + rs.unsetMatchColumn(1); + actualCols = rs.getMatchColumnIndexes(); + rs.close(); + }); } /* * Validate that getMatchColumnNames throws a SQLException if * unsetMatchColumn has been called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0021(CachedRowSet rs) throws Exception { - String matchColumn = "ID"; - rs.setMatchColumn(matchColumn); - String[] actualColNames = rs.getMatchColumnNames(); - assertTrue(actualColNames != null); - rs.unsetMatchColumn(matchColumn); - actualColNames = rs.getMatchColumnNames(); - rs.close(); + assertThrows(SQLException.class, () -> { + String matchColumn = "ID"; + rs.setMatchColumn(matchColumn); + String[] actualColNames = rs.getMatchColumnNames(); + assertTrue(actualColNames != null); + rs.unsetMatchColumn(matchColumn); + actualColNames = rs.getMatchColumnNames(); + rs.close(); + }); } /* * Validate that getMatchColumnIndexes throws a SQLException if * unsetMatchColumn has been called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0022(CachedRowSet rs) throws Exception { - int[] expectedCols = {1, 3}; - rs.setMatchColumn(expectedCols); - int[] actualCols = rs.getMatchColumnIndexes(); - assertTrue(actualCols != null); - rs.unsetMatchColumn(expectedCols); - actualCols = rs.getMatchColumnIndexes(); - rs.close(); + assertThrows(SQLException.class, () -> { + int[] expectedCols = {1, 3}; + rs.setMatchColumn(expectedCols); + int[] actualCols = rs.getMatchColumnIndexes(); + assertTrue(actualCols != null); + rs.unsetMatchColumn(expectedCols); + actualCols = rs.getMatchColumnIndexes(); + rs.close(); + }); } /* * Validate that getMatchColumnNames throws a SQLException if * unsetMatchColumn has been called */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0023(CachedRowSet rs) throws Exception { - String[] expectedColNames = {"COF_ID", "SUP_ID"}; - rs.setMatchColumn(expectedColNames); - String[] actualColNames = rs.getMatchColumnNames(); - assertTrue(actualColNames != null); - rs.unsetMatchColumn(expectedColNames); - actualColNames = rs.getMatchColumnNames(); - rs.close(); + assertThrows(SQLException.class, () -> { + String[] expectedColNames = {"COF_ID", "SUP_ID"}; + rs.setMatchColumn(expectedColNames); + String[] actualColNames = rs.getMatchColumnNames(); + assertTrue(actualColNames != null); + rs.unsetMatchColumn(expectedColNames); + actualColNames = rs.getMatchColumnNames(); + rs.close(); + }); } /* * Validate size() returns the correct number of rows */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0024(CachedRowSet rs) throws Exception { assertTrue(rs.size() == COFFEE_HOUSES_ROWS); rs.close(); @@ -708,9 +747,10 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that the correct rows are returned comparing the primary * keys */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0025(RowSet rs) throws SQLException { - assertEquals(getPrimaryKeys(rs), COFFEE_HOUSES_PRIMARY_KEYS); + assertArrayEquals(COFFEE_HOUSES_PRIMARY_KEYS, getPrimaryKeys(rs)); rs.close(); } @@ -719,7 +759,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate the visibility of the row depending on the value of * setShowdelete */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0026(CachedRowSet rs) throws Exception { Object[] afterDelete = { 10023, 33002, 10040, 32001, 10042, 10024, 10039, 10041, @@ -727,13 +768,13 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { }; int rowToDelete = 10035; // All rows should be found - assertEquals(getPrimaryKeys(rs), COFFEE_HOUSES_PRIMARY_KEYS); + assertArrayEquals(COFFEE_HOUSES_PRIMARY_KEYS, getPrimaryKeys(rs)); // Delete the row assertTrue(deleteRowByPrimaryKey(rs, rowToDelete, 1)); // With setShowDeleted(false) which is the default, // the deleted row should not be visible assertFalse(findRowByPrimaryKey(rs, rowToDelete, 1)); - assertEquals(getPrimaryKeys(rs), afterDelete); + assertArrayEquals(afterDelete, getPrimaryKeys(rs)); assertTrue(rs.size() == COFFEE_HOUSES_ROWS); // With setShowDeleted(true), the deleted row should be visible rs.setShowDeleted(true); @@ -744,7 +785,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that there is no page size by default */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0027(CachedRowSet rs) throws Exception { assertTrue(rs.getPageSize() == 0); rs.close(); @@ -754,7 +796,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate the value you set via setPageSize is returned by getPageSize * then reset to having no limit */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0028(CachedRowSet rs) throws Exception { int rows = 100; rs.setPageSize(rows); @@ -768,30 +811,39 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate SQLException is thrown when an invalid value is specified * for setPageSize */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0029(CachedRowSet rs) throws Exception { - rs.setPageSize(-1); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setPageSize(-1); + rs.close(); + }); } /* * Validate SQLException is thrown when nextPage is called without a * call to populate or execute */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0030(CachedRowSet rs) throws Exception { - rs.nextPage(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.nextPage(); + rs.close(); + }); } /* * Validate SQLException is thrown when previousPage is called without a * call to populate or execute */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0031(CachedRowSet rs) throws Exception { - rs.previousPage(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.previousPage(); + rs.close(); + }); } @@ -799,40 +851,48 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate SQLException is thrown when execute is called * but there is not a way to make a connection to the datasource */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0032(CachedRowSet rs) throws Exception { - rs.execute(null); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.execute(null); + rs.close(); + }); } /* * Validate SQLException is thrown when execute is called * but there is not a way to make a connection to the datasource */ - @Test(dataProvider = "rowSetType", expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0033(CachedRowSet rs) throws Exception { - rs.execute(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.execute(); + rs.close(); + }); } /* * Validate that toCollection() returns the proper values */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0034(CachedRowSet rs) throws Exception { Object[] cities = {"Mendocino", "Seattle", "SF", "Portland", "SF", "Sacramento", "Carmel", "LA", "Olympia", "Seattle", "SF", "LA", "San Jose", "Eugene"}; rs.beforeFirst(); - assertEquals(rs.toCollection(2).toArray(), cities); - assertEquals(rs.toCollection("CITY").toArray(), cities); + assertArrayEquals(cities, rs.toCollection(2).toArray()); + assertArrayEquals(cities, rs.toCollection("CITY").toArray()); rs.close(); } /* * Validate that toCollection() returns the proper values */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0035(CachedRowSet rs) throws Exception { Collection col = rs.toCollection(); assertTrue(rs.size() == col.size()); @@ -850,7 +910,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that createCopy() returns the proper values */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0036(CachedRowSet rs) throws Exception { try (CachedRowSet crs1 = rs.createCopy()) { compareRowSets(rs, crs1); @@ -861,7 +922,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { /* * Validate that createCopySchema() returns the proper values */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0037(CachedRowSet rs) throws Exception { try (CachedRowSet crs1 = rs.createCopySchema()) { assertTrue(crs1.size() == 0); @@ -875,7 +937,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * and getMatchColumnIndexes should throw a SQLException. This test * specifies setMatchColumn(int) */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0038(CachedRowSet rs) throws Exception { rs.setMatchColumn(1); try (CachedRowSet crs1 = rs.createCopyNoConstraints()) { @@ -904,7 +967,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * and getMatchColumnIndexes should throw a SQLException. This test * specifies setMatchColumn(String) */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0039(CachedRowSet rs) throws Exception { rs.setMatchColumn("ID"); try (CachedRowSet crs1 = rs.createCopyNoConstraints()) { @@ -932,7 +996,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that columnUpdated works with the various datatypes specifying * the column index */ - @Test(dataProvider = "rowsetUsingDataTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingDataTypes") public void commonCachedRowSetTest0040(CachedRowSet rs, JDBCType type) throws Exception { rs.beforeFirst(); assertTrue(rs.next()); @@ -1029,7 +1094,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that columnUpdated works with the various datatypes specifying * the column name */ - @Test(dataProvider = "rowsetUsingDataTypes") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingDataTypes") public void commonCachedRowSetTest0041(CachedRowSet rs, JDBCType type) throws Exception { rs.beforeFirst(); assertTrue(rs.next()); @@ -1127,7 +1193,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate isBeforeFirst(), isFirst() and first() return the correct * results */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0042(RowSet rs) throws Exception { assertFalse(rs.isBeforeFirst()); assertFalse(rs.isFirst()); @@ -1150,7 +1217,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate isAfterLast(), isLast() and last() return the correct * results */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0043(RowSet rs) throws Exception { assertFalse(rs.isAfterLast()); assertFalse(rs.isLast()); @@ -1173,121 +1241,140 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate a SQLException is thrown when undoDelete is called on the * insertRow */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0044(CachedRowSet rs) throws Exception { - rs.insertRow(); - rs.undoDelete(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.insertRow(); + rs.undoDelete(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoDelete is called when * cursor is before the first row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0045(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.beforeFirst(); - rs.undoDelete(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.beforeFirst(); + rs.undoDelete(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoDelete is called when * cursor is after the last row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0046(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.afterLast(); - rs.undoDelete(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.afterLast(); + rs.undoDelete(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoUpdate is called on the * insertRow */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0047(CachedRowSet rs) throws Exception { - rs.insertRow(); - rs.undoUpdate(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.insertRow(); + rs.undoUpdate(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoUpdate is called when * cursor is before the first row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0048(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.beforeFirst(); - rs.undoUpdate(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.beforeFirst(); + rs.undoUpdate(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoUpdate is called when * cursor is after the last row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0049(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.afterLast(); - rs.undoUpdate(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.afterLast(); + rs.undoUpdate(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoInsert is called on the * insertRow */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0050(CachedRowSet rs) throws Exception { - rs.insertRow(); - rs.undoInsert(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.insertRow(); + rs.undoInsert(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoInsert is called when * cursor is before the first row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0051(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.beforeFirst(); - rs.undoInsert(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.beforeFirst(); + rs.undoInsert(); + rs.close(); + }); } /* * Validate a SQLException is thrown when undoInsert is called when * cursor is after the last row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses", - expectedExceptions = SQLException.class) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0052(CachedRowSet rs) throws Exception { - rs.setShowDeleted(true); - rs.afterLast(); - rs.undoInsert(); - rs.close(); + assertThrows(SQLException.class, () -> { + rs.setShowDeleted(true); + rs.afterLast(); + rs.undoInsert(); + rs.close(); + }); } /* * Insert a row, then call undoInsert to roll back the insert and validate * the row is not there */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0053(CachedRowSet rs) throws Exception { int rowToInsert = 1961; assertTrue(rs.size() == COFFEE_HOUSES_ROWS); @@ -1314,7 +1401,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Insert a row, delete the row and then call undoDelete to make sure it * is comes back */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0054(CachedRowSet rs) throws Exception { int rowToDelete = 1961; assertTrue(rs.size() == COFFEE_HOUSES_ROWS); @@ -1348,7 +1436,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Insert a row, modify a field and then call undoUpdate to revert the * insert */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0055(CachedRowSet rs) throws Exception { int rowToInsert = 1961; assertTrue(rs.size() == COFFEE_HOUSES_ROWS); @@ -1385,7 +1474,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate getOriginal returns a ResultSet which is a copy of the original * RowSet */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void commonCachedRowSetTest0056(CachedRowSet rs) throws Exception { String coffee = "Hazelnut"; int sales = 100; @@ -1414,12 +1504,12 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { assertTrue(rs1.getInt(1) == origId); assertTrue(rs1.getString(2).equals(origCoffee)); assertTrue(rs1.getInt(5) == origSales); - assertEquals(getPrimaryKeys(rs1), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(rs1)); // Check current rowset assertTrue(rs.getInt(1) == id); assertTrue(rs.getString(2).equals(coffee)); assertTrue(rs.getInt(5) == sales); - assertEquals(getPrimaryKeys(rs), updatedPkeys); + assertArrayEquals(updatedPkeys, getPrimaryKeys(rs)); } rs.close(); } @@ -1428,7 +1518,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate getOriginalRow returns a ResultSet which is a copy of the * original row that was modified */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void commonCachedRowSetTest0057(CachedRowSet rs) throws Exception { String coffee = "Hazelnut"; int sales = 100; @@ -1463,7 +1554,7 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { assertTrue(rs.getInt(1) == id); assertTrue(rs.getString(2).equals(coffee)); assertTrue(rs.getInt(5) == sales); - assertEquals(getPrimaryKeys(rs), updatedPkeys); + assertArrayEquals(updatedPkeys, getPrimaryKeys(rs)); } rs.close(); } @@ -1472,7 +1563,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that restoreOrginal will restore the RowSet to its * state prior to the insert of a row */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0058(CachedRowSet rs) throws Exception { int rowToInsert = 1961; assertTrue(rs.size() == COFFEE_HOUSES_ROWS); @@ -1509,7 +1601,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that restoreOrginal will restore the RowSet to its * state prior to deleting a row */ - @Test(dataProvider = "rowsetUsingCoffees", enabled = true) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void commonCachedRowSetTest0059(CachedRowSet rs) throws Exception { int rowToDelete = 2; try (CachedRowSet crs1 = rsf.createCachedRowSet()) { @@ -1540,7 +1633,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate that restoreOrginal will restore the RowSet to its * state prior to updating a row */ - @Test(dataProvider = "rowsetUsingCoffees", enabled = true) + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void commonCachedRowSetTest0060(CachedRowSet rs) throws Exception { int rowToUpdate = 2; String coffee = "Hazelnut"; @@ -1578,7 +1672,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Initialize a RowSet via the populate method. Validate it matches * the original ResultSet */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0061(CachedRowSet rs) throws Exception { try (CachedRowSet crs1 = rsf.createCachedRowSet()) { rs.beforeFirst(); @@ -1593,7 +1688,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { * Validate it matches the original ResultSet starting for the specofied * offset */ - @Test(dataProvider = "rowsetUsingCoffeeHouses") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffeeHouses") public void commonCachedRowSetTest0062(CachedRowSet rs) throws Exception { Object[] expectedRows = { 32001, 10042, 10024, 10039, 10041, 33005, 33010, 10035, 10037, @@ -1603,8 +1699,8 @@ public abstract class CommonCachedRowSetTests extends CommonRowSetTests { try (CachedRowSet crs1 = rsf.createCachedRowSet()) { rs.beforeFirst(); crs1.populate(rs, startingRow); - assertEquals(crs1.size(), COFFEE_HOUSES_ROWS - startingRow + 1); - assertEquals(getPrimaryKeys(crs1), expectedRows); + assertEquals(COFFEE_HOUSES_ROWS - startingRow + 1, crs1.size()); + assertArrayEquals(expectedRows, getPrimaryKeys(crs1)); } rs.close(); } diff --git a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/CityFilter.java b/test/jdk/javax/sql/test/rowset/filteredrowset/CityFilter.java similarity index 97% rename from test/jdk/javax/sql/testng/test/rowset/filteredrowset/CityFilter.java rename to test/jdk/javax/sql/test/rowset/filteredrowset/CityFilter.java index 2188c4eed08..458088ccb45 100644 --- a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/CityFilter.java +++ b/test/jdk/javax/sql/test/rowset/filteredrowset/CityFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 diff --git a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/FilteredRowSetTests.java b/test/jdk/javax/sql/test/rowset/filteredrowset/FilteredRowSetTests.java similarity index 81% rename from test/jdk/javax/sql/testng/test/rowset/filteredrowset/FilteredRowSetTests.java rename to test/jdk/javax/sql/test/rowset/filteredrowset/FilteredRowSetTests.java index 263a79a68b6..55d9323f373 100644 --- a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/FilteredRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/filteredrowset/FilteredRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 @@ -26,24 +26,30 @@ import java.sql.SQLException; import javax.sql.RowSet; import javax.sql.rowset.FilteredRowSet; import javax.sql.rowset.Predicate; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterEach; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import test.rowset.webrowset.CommonWebRowSetTests; public class FilteredRowSetTests extends CommonWebRowSetTests { private FilteredRowSet frs; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { frs = createCoffeeHousesRowSet(); } - @AfterMethod + @AfterEach public void tearDownMethod() throws Exception { frs.close(); } @@ -83,7 +89,7 @@ public class FilteredRowSetTests extends CommonWebRowSetTests { 10023, 10040, 10042, 10024, 10039, 10041, 10035, 10037, 10034 }; frs.setFilter(new PrimaryKeyFilter(10000, 10999, 1)); - assertEquals(getPrimaryKeys(frs), expectedKeys); + assertArrayEquals(expectedKeys, getPrimaryKeys(frs)); } /* @@ -96,7 +102,7 @@ public class FilteredRowSetTests extends CommonWebRowSetTests { 10023, 10040, 10042, 10024, 10039, 10041, 10035, 10037, 10034 }; frs.setFilter(new PrimaryKeyFilter(10000, 10999, "STORE_ID")); - assertEquals(getPrimaryKeys(frs), expectedKeys); + assertArrayEquals(expectedKeys, getPrimaryKeys(frs)); } @@ -111,7 +117,7 @@ public class FilteredRowSetTests extends CommonWebRowSetTests { }; String[] cityArray = {"SF", "LA"}; frs.setFilter(new CityFilter(cityArray, 2)); - assertEquals(getPrimaryKeys(frs), expectedKeys); + assertArrayEquals(expectedKeys, getPrimaryKeys(frs)); } /* @@ -125,14 +131,16 @@ public class FilteredRowSetTests extends CommonWebRowSetTests { }; String[] cityArray = {"SF", "LA"}; frs.setFilter(new CityFilter(cityArray, "CITY")); - assertEquals(getPrimaryKeys(frs), expectedKeys); + assertArrayEquals(expectedKeys, getPrimaryKeys(frs)); } // Tests that are common but need to be disabled due to an implementation bug - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0043(RowSet rs) throws Exception { // Need to fix bug in FilteredRowSets } diff --git a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/PrimaryKeyFilter.java b/test/jdk/javax/sql/test/rowset/filteredrowset/PrimaryKeyFilter.java similarity index 97% rename from test/jdk/javax/sql/testng/test/rowset/filteredrowset/PrimaryKeyFilter.java rename to test/jdk/javax/sql/test/rowset/filteredrowset/PrimaryKeyFilter.java index 2a748314006..0353312419c 100644 --- a/test/jdk/javax/sql/testng/test/rowset/filteredrowset/PrimaryKeyFilter.java +++ b/test/jdk/javax/sql/test/rowset/filteredrowset/PrimaryKeyFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 diff --git a/test/jdk/javax/sql/testng/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java b/test/jdk/javax/sql/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java similarity index 95% rename from test/jdk/javax/sql/testng/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java rename to test/jdk/javax/sql/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java index be86798177a..5b135e16b5c 100644 --- a/test/jdk/javax/sql/testng/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java +++ b/test/jdk/javax/sql/test/rowset/jdbcrowset/JdbcRowSetDriverManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 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 @@ -28,7 +28,9 @@ import java.sql.SQLException; import javax.sql.rowset.JdbcRowSet; import javax.sql.rowset.RowSetFactory; import javax.sql.rowset.RowSetProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubDriver; @@ -41,7 +43,7 @@ public class JdbcRowSetDriverManagerTest extends BaseTest { * Validate that JDBCRowSetImpl can connect to a JDBC driver that is * register by DriverManager. */ - @Test(enabled = true) + @Test public void test0000() throws SQLException { DriverManager.registerDriver(new StubDriver()); diff --git a/test/jdk/javax/sql/testng/test/rowset/joinrowset/JoinRowSetTests.java b/test/jdk/javax/sql/test/rowset/joinrowset/JoinRowSetTests.java similarity index 83% rename from test/jdk/javax/sql/testng/test/rowset/joinrowset/JoinRowSetTests.java rename to test/jdk/javax/sql/test/rowset/joinrowset/JoinRowSetTests.java index b84453e9a3b..92ce09bcf85 100644 --- a/test/jdk/javax/sql/testng/test/rowset/joinrowset/JoinRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/joinrowset/JoinRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 @@ -26,16 +26,22 @@ import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import javax.sql.RowSet; import javax.sql.rowset.CachedRowSet; import javax.sql.rowset.JoinRowSet; import javax.sql.rowset.RowSetMetaDataImpl; import javax.sql.rowset.WebRowSet; -import static org.testng.Assert.assertEquals; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + import test.rowset.webrowset.CommonWebRowSetTests; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + public class JoinRowSetTests extends CommonWebRowSetTests { private final String SUPPLIERS_TABLE = "SUPPLIERS"; @@ -150,8 +156,7 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * DataProvider used to set parameters for basic types that are supported */ - @DataProvider(name = "createCachedRowSetsToUse") - private Object[][] createCachedRowSetsToUse() throws SQLException { + private Stream createCachedRowSetsToUse() throws SQLException { CachedRowSet crs = rsf.createCachedRowSet(); initCoffeesMetaData(crs); createCoffeesRows(crs); @@ -162,9 +167,7 @@ public class JoinRowSetTests extends CommonWebRowSetTests { createSuppiersRows(crs1); // Make sure you are not on the insertRow crs1.moveToCurrentRow(); - return new Object[][]{ - {crs, crs1} - }; + return Stream.of(Arguments.of(crs, crs1)); } /* @@ -178,13 +181,14 @@ public class JoinRowSetTests extends CommonWebRowSetTests { results.add(jrs.getInt("COF_ID")); } } - assertEquals(results.toArray(), EXPECTED); + assertArrayEquals(EXPECTED, results.toArray()); } /* * Join two CachedRowSets specifying a column name to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0000(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -200,7 +204,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column index to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0001(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -216,7 +221,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column name to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0002(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -233,7 +239,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column index to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0003(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -251,7 +258,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column name to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0005(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -269,7 +277,8 @@ public class JoinRowSetTests extends CommonWebRowSetTests { /* * Join two CachedRowSets specifying a column index to join against */ - @Test(dataProvider = "createCachedRowSetsToUse") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("createCachedRowSetsToUse") public void joinRowSetTests0006(CachedRowSet crs, CachedRowSet crs1) throws Exception { @@ -286,39 +295,56 @@ public class JoinRowSetTests extends CommonWebRowSetTests { } // Disabled tests due to bugs in JoinRowSet - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0004(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0005(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0008(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0026(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0027(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0053(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0054(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void commonCachedRowSetTest0055(CachedRowSet rs) throws Exception { } - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0009(WebRowSet wrs1) throws Exception { } } diff --git a/test/jdk/javax/sql/test/rowset/resourcebundle/TEST.properties b/test/jdk/javax/sql/test/rowset/resourcebundle/TEST.properties new file mode 100644 index 00000000000..045227a0c2b --- /dev/null +++ b/test/jdk/javax/sql/test/rowset/resourcebundle/TEST.properties @@ -0,0 +1,2 @@ +# Enabled for ValidateGetBundle.java +modules = java.sql.rowset/com.sun.rowset:+open diff --git a/test/jdk/javax/sql/resourceBundleTests/ValidateGetBundle.java b/test/jdk/javax/sql/test/rowset/resourcebundle/ValidateGetBundle.java similarity index 94% rename from test/jdk/javax/sql/resourceBundleTests/ValidateGetBundle.java rename to test/jdk/javax/sql/test/rowset/resourcebundle/ValidateGetBundle.java index be17caed5c8..63f12740c09 100644 --- a/test/jdk/javax/sql/resourceBundleTests/ValidateGetBundle.java +++ b/test/jdk/javax/sql/test/rowset/resourcebundle/ValidateGetBundle.java @@ -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 @@ -20,6 +20,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package test.rowset.resourcebundle; import com.sun.rowset.JdbcRowSetResourceBundle; @@ -35,8 +36,6 @@ import org.junit.jupiter.params.provider.Arguments; * @test * @bug 8294989 * @summary Check JDBC RowSet resource bundle access - * @modules java.sql.rowset/com.sun.rowset:+open - * @run junit/othervm ValidateGetBundle */ public class ValidateGetBundle{ diff --git a/test/jdk/javax/sql/testng/test/rowset/ValidateResourceBundleAccess.java b/test/jdk/javax/sql/test/rowset/resourcebundle/ValidateResourceBundleAccess.java similarity index 89% rename from test/jdk/javax/sql/testng/test/rowset/ValidateResourceBundleAccess.java rename to test/jdk/javax/sql/test/rowset/resourcebundle/ValidateResourceBundleAccess.java index 7591891c42b..5deca26cb25 100644 --- a/test/jdk/javax/sql/testng/test/rowset/ValidateResourceBundleAccess.java +++ b/test/jdk/javax/sql/test/rowset/resourcebundle/ValidateResourceBundleAccess.java @@ -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 @@ -20,21 +20,21 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package test.rowset; +package test.rowset.resourcebundle; import java.util.Locale; import java.sql.SQLException; import javax.sql.rowset.RowSetProvider; -import org.testng.annotations.Test; -import org.testng.annotations.BeforeClass; -import static org.testng.Assert.*; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** * @test * @bug 8294989 * @summary Check that the resource bundle can be accessed * @throws SQLException if an unexpected error occurs - * @run testng/othervm */ public class ValidateResourceBundleAccess{ // Expected JDBCResourceBundle message, jdbcrowsetimpl.invalstate @@ -43,8 +43,8 @@ public class ValidateResourceBundleAccess{ private static final String RSREADERERROR = "Internal Error in RowSetReader: no connection or command"; // Checking against English messages, set to US Locale - @BeforeClass - public void setEnglishEnvironment() { + @BeforeAll + public static void setEnglishEnvironment() { Locale.setDefault(Locale.US); } diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java b/test/jdk/javax/sql/test/rowset/serial/SQLInputImplTests.java similarity index 92% rename from test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java rename to test/jdk/javax/sql/test/rowset/serial/SQLInputImplTests.java index 5bd10ed8848..33efda61c4e 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SQLInputImplTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SQLInputImplTests.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 @@ -33,9 +33,11 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.sql.rowset.serial.SQLInputImpl; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubArray; import util.StubBlob; @@ -54,7 +56,7 @@ public class SQLInputImplTests extends BaseTest { private SuperHero hero; private final String sqlType = "SUPERHERO"; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { map = new HashMap<>(); impl = new TestSQLDataImpl("TestSQLData"); @@ -68,18 +70,22 @@ public class SQLInputImplTests extends BaseTest { * Validate that a SQLException is thrown if the attribute value is * null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test() throws Exception { - SQLInputImpl x = new SQLInputImpl(null, map); + assertThrows(SQLException.class, () -> { + SQLInputImpl x = new SQLInputImpl(null, map); + }); } /* * Validate that a SQLException is thrown if the map value is * null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test02() throws Exception { - SQLInputImpl x = new SQLInputImpl(typeValues, null); + assertThrows(SQLException.class, () -> { + SQLInputImpl x = new SQLInputImpl(typeValues, null); + }); } /* @@ -124,7 +130,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a Array can be read */ - @Test(enabled = true) + @Test public void test06() throws Exception { Object[] coffees = new Object[]{"Espresso", "Colombian", "French Roast", "Cappuccino"}; @@ -139,7 +145,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a Blob can be read */ - @Test(enabled = true) + @Test public void test07() throws Exception { Blob b = new StubBlob(); Object[] values = {b}; @@ -153,7 +159,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a Clob can be read */ - @Test(enabled = true) + @Test public void test08() throws Exception { Clob c = new StubClob(); Object[] values = {c}; @@ -166,7 +172,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a Ref can be read */ - @Test(enabled = true) + @Test public void test09() throws Exception { Ref ref = new StubRef(sqlType, hero); Object[] values = {ref}; @@ -179,7 +185,7 @@ public class SQLInputImplTests extends BaseTest { /* * Validate a URL can be read */ - @Test(enabled = true) + @Test public void test10() throws Exception { URL u = new URL("http://www.oracle.com/"); Object[] values = {u}; diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java b/test/jdk/javax/sql/test/rowset/serial/SQLOutputImplTests.java similarity index 91% rename from test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java rename to test/jdk/javax/sql/test/rowset/serial/SQLOutputImplTests.java index 1f90c9981a7..83712e81396 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SQLOutputImplTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SQLOutputImplTests.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 @@ -41,9 +41,11 @@ import javax.sql.rowset.serial.SerialClob; import javax.sql.rowset.serial.SerialDatalink; import javax.sql.rowset.serial.SerialRef; import javax.sql.rowset.serial.SerialStruct; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubArray; import util.StubBlob; @@ -64,7 +66,7 @@ public class SQLOutputImplTests extends BaseTest { private SuperHero hero; private SQLOutputImpl outImpl; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { results = new Vector(); impl = new TestSQLDataImpl("TestSQLData"); @@ -78,18 +80,22 @@ public class SQLOutputImplTests extends BaseTest { * Validate that a SQLException is thrown if the attribute value is * null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test() throws Exception { - SQLOutputImpl x = new SQLOutputImpl(null, map); + assertThrows(SQLException.class, () -> { + SQLOutputImpl x = new SQLOutputImpl(null, map); + }); } /* * Validate that a SQLException is thrown if the map value is * null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test02() throws Exception { - SQLOutputImpl x = new SQLOutputImpl(results, null); + assertThrows(SQLException.class, () -> { + SQLOutputImpl x = new SQLOutputImpl(results, null); + }); } /* @@ -109,7 +115,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Array can be written and returned */ - @Test(enabled = true) + @Test public void test04() throws Exception { Object[] coffees = new Object[]{"Espresso", "Colombian", "French Roast", "Cappuccino"}; @@ -123,7 +129,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Blob can be written and returned */ - @Test(enabled = true) + @Test public void test05() throws Exception { Blob b = new StubBlob(); outImpl.writeBlob(b); @@ -136,7 +142,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Clob can be written and returned */ - @Test(enabled = true) + @Test public void test06() throws Exception { Clob c = new StubClob(); outImpl.writeClob(c); @@ -148,7 +154,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Ref can be written and returned */ - @Test(enabled = true) + @Test public void test07() throws Exception { Ref ref = new StubRef(sqlType, hero); outImpl.writeRef(ref); @@ -159,7 +165,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a Struct can be written and returned */ - @Test(enabled = true) + @Test public void test08() throws Exception { Object[] attributes = new Object[]{"Bruce", "Wayne", 1939, "Batman"}; @@ -173,7 +179,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate a DataLink can be written and returned */ - @Test(enabled = true) + @Test public void test09() throws Exception { URL u = new URL("http://www.oracle.com/"); outImpl.writeURL(u); @@ -186,7 +192,7 @@ public class SQLOutputImplTests extends BaseTest { /* * Validate an Object implementing SQLData can be written and returned */ - @Test(enabled = true) + @Test public void test10() throws Exception { Object[] attributes = new Object[]{"Bruce", "Wayne", 1939, "Batman"}; diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialArrayTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialArrayTests.java similarity index 72% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialArrayTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialArrayTests.java index 345315b7f05..892de9416a1 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialArrayTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialArrayTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -29,9 +29,11 @@ import java.util.HashMap; import java.util.Map; import javax.sql.rowset.serial.SerialArray; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubArray; @@ -42,7 +44,7 @@ public class SerialArrayTests extends BaseTest { private Array a; private Map> map; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { coffees = new Object[]{"Espresso", "Colombian", "French Roast", "Cappuccino"}; @@ -61,111 +63,133 @@ public class SerialArrayTests extends BaseTest { /* * Validate a SQLException is thrown if the map is null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test02() throws Exception { - SerialArray sa = new SerialArray(a, null); + assertThrows(SQLException.class, () -> { + SerialArray sa = new SerialArray(a, null); + }); } /* * Validate a SerialException is thrown when getResultSet() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test03() throws Exception { - SerialArray sa = new SerialArray(a); - sa.getResultSet(); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.getResultSet(); + }); } /* * Validate a SerialException is thrown when getResultSet() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test04() throws Exception { - SerialArray sa = new SerialArray(a); - sa.getResultSet(null); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.getResultSet(null); + }); } /* * Validate a SerialException is thrown when getResultSet() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test05() throws Exception { - SerialArray sa = new SerialArray(a); - sa.getResultSet(1, 1); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.getResultSet(1, 1); + }); } /* * Validate a SerialException is thrown when getResultSet() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test06() throws Exception { - SerialArray sa = new SerialArray(a); - sa.getResultSet(1, 1, null); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.getResultSet(1, 1, null); + }); } /* * Validate a SerialException is thrown when getArray() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test07() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getArray(); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getArray(); + }); } /* * Validate a SerialException is thrown when getArray() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test08() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getArray(map); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getArray(map); + }); } /* * Validate a SerialException is thrown when getArray() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test09() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getArray(1, 1, map); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getArray(1, 1, map); + }); } /* * Validate a SerialException is thrown when getArray() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test10() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getArray(1, 1); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getArray(1, 1); + }); } /* * Validate a SerialException is thrown when getBaseType() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test11() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getBaseType(); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getBaseType(); + }); } /* * Validate a SerialException is thrown when getBaseTypeName() is invoked after * free() is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test12() throws Exception { - SerialArray sa = new SerialArray(a); - sa.free(); - sa.getBaseTypeName(); + assertThrows(SerialException.class, () -> { + SerialArray sa = new SerialArray(a); + sa.free(); + sa.getBaseTypeName(); + }); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialBlobTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialBlobTests.java similarity index 69% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialBlobTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialBlobTests.java index f3f6c327683..f4a32bc19d7 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialBlobTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialBlobTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, 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 @@ -24,11 +24,14 @@ package test.rowset.serial; import java.io.InputStream; import java.io.OutputStream; +import java.sql.SQLException; import java.util.Arrays; import javax.sql.rowset.serial.SerialBlob; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubBlob; @@ -37,6 +40,16 @@ public class SerialBlobTests extends BaseTest { // byte[] used to populate SerialBlob private byte[] bytes = new byte[]{1, 2, 3, 4, 5}; + /* + * Validate calling setBinaryStream() on a SerialBlob constructed from a + * byte array throws SerialException. Bug 7077451. + */ + @Test + void setBinaryStreamExceptionTest() throws SQLException { + SerialBlob blob = new SerialBlob(new byte[0]); + assertThrows(SerialException.class, () -> blob.setBinaryStream(0)); + } + /* * Validate calling free() does not throw an Exception */ @@ -50,110 +63,130 @@ public class SerialBlobTests extends BaseTest { * Validate calling getBinaryStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test01() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.getBinaryStream(); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.getBinaryStream(); + }); } /* * Validate calling getBinaryStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test02() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.getBinaryStream(1, 5); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.getBinaryStream(1, 5); + }); } /* * Validate calling getBytes() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test03() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.getBytes(1, 1); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.getBytes(1, 1); + }); } /* * Validate calling getLength() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test04() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.length(); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.length(); + }); } /* * Validate calling position() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test05() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.position(new byte[5], 1); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.position(new byte[5], 1); + }); } /* * Validate calling position() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test06() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.position(new StubBlob(), 1); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.position(new StubBlob(), 1); + }); } /* * Validate calling free() after calling setBinaryStream() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test07() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.setBinaryStream(5); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.setBinaryStream(5); + }); } /* * Validate calling free() after calling setBytes() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test08() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.setBytes(1, new byte[5]); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.setBytes(1, new byte[5]); + }); } /* * Validate calling setBytes() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test09() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.setBytes(1, new byte[10], 0, 5); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.setBytes(1, new byte[10], 0, 5); + }); } /* * Validate calling truncate() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test10() throws Exception { - SerialBlob sb = new SerialBlob(new StubBlob()); - sb.free(); - sb.truncate(1); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(new StubBlob()); + sb.free(); + sb.truncate(1); + }); } /* @@ -173,56 +206,68 @@ public class SerialBlobTests extends BaseTest { /* * Validate a SerialException is thrown if pos < 0 for getBinaryStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test12() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(-1, 3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(-1, 3); + }); } /* * Validate a SerialException is thrown if pos = 0 for getBinaryStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test13() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(0, 3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(0, 3); + }); } /* * Validate a SerialException is thrown if len > the length of the stream * for getBinaryStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test14() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(0, 3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(0, 3); + }); } /* * Validate a SerialException is thrown if length < 1 */ - @Test(expectedExceptions = SerialException.class) + @Test public void test15() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(1, 0); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(1, 0); + }); } /* * Validate a SerialException is thrown if length > byte array length */ - @Test(expectedExceptions = SerialException.class) + @Test public void test16() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(1, 6); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(1, 6); + }); } /* * Validate a SerialException is thrown if pos > byte array length */ - @Test(expectedExceptions = SerialException.class) + @Test public void test17() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - InputStream is = sb.getBinaryStream(bytes.length + 2, 6); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + InputStream is = sb.getBinaryStream(bytes.length + 2, 6); + }); } /* @@ -241,12 +286,14 @@ public class SerialBlobTests extends BaseTest { /* * Test clone after free has been called that the clone is not accessible */ - @Test(expectedExceptions = SerialException.class) + @Test public void test19() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - sb.free(); - SerialBlob sb2 = (SerialBlob) sb.clone(); - InputStream is = sb2.getBinaryStream(1, 3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + sb.free(); + SerialBlob sb2 = (SerialBlob) sb.clone(); + InputStream is = sb2.getBinaryStream(1, 3); + }); } /* @@ -264,10 +311,12 @@ public class SerialBlobTests extends BaseTest { * Validate a SerialException is thrown if byte[] is used to * create the SeriablBlob and setBinaryStream is called */ - @Test(expectedExceptions = SerialException.class) + @Test public void test21() throws Exception { - SerialBlob sb = new SerialBlob(bytes); - sb.setBinaryStream(3); + assertThrows(SerialException.class, () -> { + SerialBlob sb = new SerialBlob(bytes); + sb.setBinaryStream(3); + }); } /* @@ -281,7 +330,7 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 7, 8, 9, 5}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(2, diff); - assertEquals(written, diff.length); + assertEquals(diff.length, written); assertTrue( Arrays.equals(sb.getBytes(1, (int) sb.length()), expected), @@ -300,7 +349,7 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 8, 9, 0, 5}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(2, diff, 1, bytesToWrite); - assertEquals(written, bytesToWrite); + assertEquals(bytesToWrite, written); assertTrue( Arrays.equals(sb.getBytes(1, (int) sb.length()), expected), @@ -355,7 +404,7 @@ public class SerialBlobTests extends BaseTest { byte[] pattern = new byte[]{3, 4}; SerialBlob sb = new SerialBlob(bytes); long pos = sb.position(pattern, 1); - assertEquals(pos, expectedPos); + assertEquals(expectedPos, pos); } /* @@ -368,7 +417,7 @@ public class SerialBlobTests extends BaseTest { byte[] pattern = new byte[]{3, 4, 5}; SerialBlob sb = new SerialBlob(bytes); long pos = sb.position(pattern, 2); - assertEquals(pos, expectedPos); + assertEquals(expectedPos, pos); } /* @@ -381,7 +430,7 @@ public class SerialBlobTests extends BaseTest { byte[] pattern = new byte[]{4, 6}; SerialBlob sb = new SerialBlob(new StubBlob()); long pos = sb.position(pattern, 1); - assertEquals(pos, expectedPos); + assertEquals(expectedPos, pos); } /* @@ -394,7 +443,7 @@ public class SerialBlobTests extends BaseTest { byte[] pattern = new byte[]{6, 8}; SerialBlob sb = new SerialBlob(new StubBlob()); long pos = sb.position(pattern, 2); - assertEquals(pos, expectedPos); + assertEquals(expectedPos, pos); } /* @@ -411,8 +460,8 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 2, 3, 4, 7}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(writePos, diff, 0, bytesToWrite); - assertEquals(written, bytesToWrite); - assertEquals(sb.getBytes(1, (int) sb.length()), expected); + assertEquals(bytesToWrite, written); + assertArrayEquals(expected, sb.getBytes(1, (int) sb.length())); } /* @@ -428,8 +477,8 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 2, 3, 4, 8, 9}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(writePos, diff, 1, bytesToWrite); - assertEquals(written, bytesToWrite); - assertEquals(sb.getBytes(1, (int) sb.length()), expected); + assertEquals(bytesToWrite, written); + assertArrayEquals(expected, sb.getBytes(1, (int) sb.length())); } /* @@ -445,29 +494,33 @@ public class SerialBlobTests extends BaseTest { byte[] expected = new byte[]{1, 2, 3, 4, 5, 8, 9, 0}; SerialBlob sb = new SerialBlob(bytes); int written = sb.setBytes(writePos, diff, 1, bytesToWrite); - assertEquals(written, bytesToWrite); - assertEquals(sb.getBytes(1, (int) sb.length()), expected); + assertEquals(bytesToWrite, written); + assertArrayEquals(expected, sb.getBytes(1, (int) sb.length())); } /* * Validate a SerialException is thrown if length < 0 for setBytes */ - @Test(expectedExceptions = SerialException.class) + @Test public void test34() throws Exception { - int length = -1; - SerialBlob sb = new SerialBlob(bytes); - int written = sb.setBytes(1, new byte[]{1}, 1, length); + assertThrows(SerialException.class, () -> { + int length = -1; + SerialBlob sb = new SerialBlob(bytes); + int written = sb.setBytes(1, new byte[]{1}, 1, length); + }); } /* * Validate a SerialException is thrown if length + offset > * Integer.MAX_VALUE for setBytes */ - @Test(expectedExceptions = SerialException.class) + @Test public void test35() throws Exception { - int offset = 1; - int length = Integer.MAX_VALUE; - SerialBlob sb = new SerialBlob(bytes); - int written = sb.setBytes(1, new byte[]{1, 2, 3}, offset, length); + assertThrows(SerialException.class, () -> { + int offset = 1; + int length = Integer.MAX_VALUE; + SerialBlob sb = new SerialBlob(bytes); + int written = sb.setBytes(1, new byte[]{1, 2, 3}, offset, length); + }); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialClobTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialClobTests.java similarity index 72% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialClobTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialClobTests.java index 95c5f6f64a7..2636c3b6a84 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialClobTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialClobTests.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 @@ -26,10 +26,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; +import java.sql.SQLException; import javax.sql.rowset.serial.SerialClob; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubClob; @@ -43,6 +47,26 @@ public class SerialClobTests extends BaseTest { 'o', 'r', 'l', 'd'}; } + /* + * Validate calling setAsciiStream() on a SerialClob constructed from a + * char array throws SerialException. Bug 7077451. + */ + @Test + void setAsciiStreamExceptionTest() throws SQLException { + SerialClob clob = new SerialClob(new char[0]); + assertThrows(SerialException.class, () -> clob.setAsciiStream(0)); + } + + /* + * Validate calling setCharacterStream() on a SerialClob constructed from a + * char array throws SerialException. Bug 7077451. + */ + @Test + void setCharacterStreamExceptionTest() throws SQLException { + SerialClob clob = new SerialClob(new char[0]); + assertThrows(SerialException.class, () -> clob.setCharacterStream(0)); + } + /* * Validate calling free() does not throw an Exception */ @@ -56,192 +80,228 @@ public class SerialClobTests extends BaseTest { * Validate calling getCharacterStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test01() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.getCharacterStream(); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.getCharacterStream(); + }); } /* * Validate calling getCharacterStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test02() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.free(); - sc.getCharacterStream(); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.free(); + sc.getCharacterStream(); + }); } /* * Validate calling getCharacterStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test03() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.getCharacterStream(1, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.getCharacterStream(1, 5); + }); } /* * Validate calling getSubString() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test04() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.getSubString(1, 1); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.getSubString(1, 1); + }); } /* * Validate calling truncate() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test05() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.truncate(1); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.truncate(1); + }); } /* * Validate calling getAsciiStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test06() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.getAsciiStream(); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.getAsciiStream(); + }); } /* * Validate calling length() after calling free() throws an SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test07() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.length(); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.length(); + }); } /* * Validate calling position() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test08() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.position("hello", 1); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.position("hello", 1); + }); } /* * Validate calling position() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test09() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.position(new StubClob(), 1); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.position(new StubClob(), 1); + }); } /* * Validate calling setAsciiStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test10() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.setAsciiStream(5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.setAsciiStream(5); + }); } /* * Validate calling setCharacterStream() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test11() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.setCharacterStream(5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.setCharacterStream(5); + }); } /* * Validate calling setString() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test12() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.setString(1, "hello"); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.setString(1, "hello"); + }); } /* * Validate calling setString() after calling free() throws an * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test13() throws Exception { - SerialClob sc = new SerialClob(new StubClob()); - sc.free(); - sc.setString(1, "hello", 0, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(new StubClob()); + sc.free(); + sc.setString(1, "hello", 0, 5); + }); } /* * Test that SerialException is thrown if pos < 0 on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test14() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(-1, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(-1, 5); + }); } /* * Test that SerialException is thrown if pos = 0 on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test15() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(0, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(0, 5); + }); } /* * Test that SerialException is thrown if pos = 0 on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test16() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(1, 100); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(1, 100); + }); } /* * Test that SerialException is thrown if length = 0 on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test17() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(1, 0); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(1, 0); + }); } /* * Test that SerialException is thrown if pos > length on a call to * getCharacterStream */ - @Test(expectedExceptions = SerialException.class) + @Test public void test18() throws Exception { - SerialClob sc = new SerialClob(chars); - sc.getCharacterStream(100, 5); + assertThrows(SerialException.class, () -> { + SerialClob sc = new SerialClob(chars); + sc.getCharacterStream(100, 5); + }); } /* @@ -401,7 +461,7 @@ public class SerialClobTests extends BaseTest { String expected = "Hello, I am the Batman!"; SerialClob sc = new SerialClob(val.toCharArray()); int written = sc.setString(13, val1); - assertEquals(val1.length(), written); + assertEquals(written, val1.length()); assertTrue(expected.equals(sc.getSubString(1, (int) sc.length()))); } @@ -420,8 +480,8 @@ public class SerialClobTests extends BaseTest { String expected = "Hi, I am the Joker!!!!!"; SerialClob sc = new SerialClob(val.toCharArray()); int written = sc.setString(writePos, val1, offset, expectedWritten); - assertEquals(written, expectedWritten); - assertEquals(sc.getSubString(1, (int) sc.length()), expected); + assertEquals(expectedWritten, written); + assertEquals(expected, sc.getSubString(1, (int) sc.length())); } /* @@ -478,7 +538,8 @@ public class SerialClobTests extends BaseTest { * Check that getCharacterStream() returns a Reader and that the char[] that * was specified to create the SerialClob can be returned via the Reader */ - @Test(enabled = false) + @Test + @Disabled public void test37() throws Exception { SerialClob sc = new SerialClob(chars); String expected = "ello w"; @@ -504,12 +565,14 @@ public class SerialClobTests extends BaseTest { * Check calling setString() with offset > val1.length() throws a * SerialException */ - @Test(expectedExceptions = SerialException.class) + @Test public void test39() throws Exception { - String val1 = "hello"; - int offset = val1.length() + 1; - SerialClob sc = new SerialClob(chars); - sc.setString(1, val1, offset, 0); + assertThrows(SerialException.class, () -> { + String val1 = "hello"; + int offset = val1.length() + 1; + SerialClob sc = new SerialClob(chars); + sc.setString(1, val1, offset, 0); + }); } /* @@ -526,8 +589,8 @@ public class SerialClobTests extends BaseTest { String expected = "Hello, I am the Joker, who are you?!"; SerialClob sc = new SerialClob(val.toCharArray()); int written = sc.setString(writePos, val1, offset, expectedWritten); - assertEquals(written, expectedWritten); - assertEquals(sc.getSubString(1, (int) sc.length()), expected); + assertEquals(expectedWritten, written); + assertEquals(expected, sc.getSubString(1, (int) sc.length())); } /* @@ -544,29 +607,33 @@ public class SerialClobTests extends BaseTest { String expected = "Hi, I am the Joker!"; SerialClob sc = new SerialClob(val.toCharArray()); int written = sc.setString(writePos, val1, offset, expectedWritten); - assertEquals(written, expectedWritten); - assertEquals(sc.getSubString(1, (int) sc.length()), expected); + assertEquals(expectedWritten, written); + assertEquals(expected, sc.getSubString(1, (int) sc.length())); } /* * Validate a SerialException is thrown if length < 0 for setString */ - @Test(expectedExceptions = SerialException.class) + @Test public void test42() throws Exception { - int length = -1; - SerialClob sc = new SerialClob(chars); - int written = sc.setString(1, "hello", 1, length); + assertThrows(SerialException.class, () -> { + int length = -1; + SerialClob sc = new SerialClob(chars); + int written = sc.setString(1, "hello", 1, length); + }); } /* * Validate a SerialException is thrown if length + offset > * Integer.MAX_VALUE for setString */ - @Test(expectedExceptions = SerialException.class) + @Test public void test43() throws Exception { - int offset = 1; - int length = Integer.MAX_VALUE; - SerialClob sc = new SerialClob(chars); - int written = sc.setString(1, "hello", offset, length); + assertThrows(SerialException.class, () -> { + int offset = 1; + int length = Integer.MAX_VALUE; + SerialClob sc = new SerialClob(chars); + int written = sc.setString(1, "hello", offset, length); + }); } } diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialDataLinkTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialDataLinkTests.java similarity index 90% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialDataLinkTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialDataLinkTests.java index 0c7f19e85b1..12c1341161d 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialDataLinkTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialDataLinkTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -25,9 +25,11 @@ package test.rowset.serial; import java.net.URL; import javax.sql.rowset.serial.SerialDatalink; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SerialDataLinkTests extends BaseTest { @@ -36,7 +38,7 @@ public class SerialDataLinkTests extends BaseTest { private URL u1; private SerialDatalink dl; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { u = new URL("http://www.oracle.com/"); u1 = new URL("http://www.usatoday.com/"); @@ -46,9 +48,11 @@ public class SerialDataLinkTests extends BaseTest { /* * Validate that a SerialException is thrown if the URL is null */ - @Test(expectedExceptions = SerialException.class) + @Test public void test() throws Exception { - SerialDatalink dl1 = new SerialDatalink(null); + assertThrows(SerialException.class, () -> { + SerialDatalink dl1 = new SerialDatalink(null); + }); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialExceptionTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialExceptionTests.java similarity index 95% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialExceptionTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialExceptionTests.java index 8ef215584a9..51c733b995a 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialExceptionTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -24,8 +24,10 @@ package test.rowset.serial; import java.sql.SQLException; import javax.sql.rowset.serial.SerialException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SerialExceptionTests extends BaseTest { diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialJavaObjectTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialJavaObjectTests.java similarity index 86% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialJavaObjectTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialJavaObjectTests.java index c2fcf2fa48f..e521125df97 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialJavaObjectTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialJavaObjectTests.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 @@ -27,8 +27,11 @@ import java.util.Arrays; import javax.sql.rowset.RowSetMetaDataImpl; import javax.sql.rowset.serial.SerialException; import javax.sql.rowset.serial.SerialJavaObject; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SerialJavaObjectTests extends BaseTest { @@ -37,18 +40,23 @@ public class SerialJavaObjectTests extends BaseTest { * Validate that an NPE is thrown when null is specified to create * the SerialJavaObject */ - @Test(expectedExceptions = NullPointerException.class) + @Test public void test() throws Exception { - SerialJavaObject sjo = new SerialJavaObject(null); + assertThrows(NullPointerException.class, () -> { + SerialJavaObject sjo = new SerialJavaObject(null); + }); } /* * Validate that a SerialException is thrown when the object specified * contains public static fields */ - @Test(expectedExceptions = SerialException.class, enabled = false) + @Test + @Disabled public void test01() throws Exception { - SerialJavaObject sjo = new SerialJavaObject(new RowSetMetaDataImpl()); + assertThrows(SerialException.class, () -> { + SerialJavaObject sjo = new SerialJavaObject(new RowSetMetaDataImpl()); + }); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialRefTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialRefTests.java similarity index 86% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialRefTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialRefTests.java index 8f23de70aa6..1b4e72593b0 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialRefTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialRefTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -27,9 +27,12 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import javax.sql.rowset.serial.SerialRef; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubRef; import util.SuperHero; @@ -41,7 +44,7 @@ public class SerialRefTests extends BaseTest { private final String sqlType = "SUPERHERO"; private SuperHero hero; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { map.put(sqlType, Class.forName("util.SuperHero")); hero = new SuperHero(sqlType, "Bruce", "Wayne", 1939, "Batman"); @@ -51,18 +54,22 @@ public class SerialRefTests extends BaseTest { /* * Validate that a SQLException() is thrown if the Ref is null */ - @Test(expectedExceptions = SQLException.class) + @Test public void test01() throws Exception { - SerialRef sr = new SerialRef(null); + assertThrows(SQLException.class, () -> { + SerialRef sr = new SerialRef(null); + }); } /* * Validate that a SQLException() is thrown if the typeName is null in the * Ref used to create the SerialRef */ - @Test(expectedExceptions = SQLException.class) + @Test public void test02() throws Exception { - SerialRef sr = new SerialRef(new StubRef(null, hero)); + assertThrows(SQLException.class, () -> { + SerialRef sr = new SerialRef(new StubRef(null, hero)); + }); } /* @@ -72,7 +79,7 @@ public class SerialRefTests extends BaseTest { @Test public void test03() throws Exception { SerialRef sr = new SerialRef(ref); - assertEquals(sr.getBaseTypeName(), sqlType); + assertEquals(sqlType, sr.getBaseTypeName()); } /* @@ -87,7 +94,8 @@ public class SerialRefTests extends BaseTest { /* * Validate that getObject() returns the same object used to create the Ref */ - @Test(enabled = false) + @Test + @Disabled public void test05() throws Exception { SerialRef sr = new SerialRef(ref); assertTrue(hero.equals(sr.getObject(map))); diff --git a/test/jdk/javax/sql/testng/test/rowset/serial/SerialStructTests.java b/test/jdk/javax/sql/test/rowset/serial/SerialStructTests.java similarity index 92% rename from test/jdk/javax/sql/testng/test/rowset/serial/SerialStructTests.java rename to test/jdk/javax/sql/test/rowset/serial/SerialStructTests.java index 79ac2421c9a..dfc34b60e2b 100644 --- a/test/jdk/javax/sql/testng/test/rowset/serial/SerialStructTests.java +++ b/test/jdk/javax/sql/test/rowset/serial/SerialStructTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -27,9 +27,11 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.sql.rowset.serial.SerialStruct; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubStruct; import util.SuperHero; @@ -42,7 +44,7 @@ public class SerialStructTests extends BaseTest { private final String sqlType = "SUPERHERO"; private SuperHero hero; - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { attributes = new Object[]{"Bruce", "Wayne", 1939, "Batman"}; @@ -58,7 +60,7 @@ public class SerialStructTests extends BaseTest { @Test public void test01() throws Exception { SerialStruct ss = new SerialStruct(struct, map); - assertEquals(ss.getSQLTypeName(), sqlType); + assertEquals(sqlType, ss.getSQLTypeName()); } /* @@ -68,7 +70,7 @@ public class SerialStructTests extends BaseTest { @Test public void test02() throws Exception { SerialStruct ss = new SerialStruct(hero, map); - assertEquals(ss.getSQLTypeName(), sqlType); + assertEquals(sqlType, ss.getSQLTypeName()); } /* diff --git a/test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryExceptionTests.java b/test/jdk/javax/sql/test/rowset/spi/SyncFactoryExceptionTests.java similarity index 95% rename from test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryExceptionTests.java rename to test/jdk/javax/sql/test/rowset/spi/SyncFactoryExceptionTests.java index e93a6109ec7..b747c48989d 100644 --- a/test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryExceptionTests.java +++ b/test/jdk/javax/sql/test/rowset/spi/SyncFactoryExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -24,8 +24,10 @@ package test.rowset.spi; import java.sql.SQLException; import javax.sql.rowset.spi.SyncFactoryException; -import static org.testng.Assert.*; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + import util.BaseTest; public class SyncFactoryExceptionTests extends BaseTest { diff --git a/test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryTests.java b/test/jdk/javax/sql/test/rowset/spi/SyncFactoryTests.java similarity index 92% rename from test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryTests.java rename to test/jdk/javax/sql/test/rowset/spi/SyncFactoryTests.java index 415488abb9f..260cf150f59 100644 --- a/test/jdk/javax/sql/testng/test/rowset/spi/SyncFactoryTests.java +++ b/test/jdk/javax/sql/test/rowset/spi/SyncFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -33,9 +33,11 @@ import javax.naming.Context; import javax.sql.rowset.spi.SyncFactory; import javax.sql.rowset.spi.SyncFactoryException; import javax.sql.rowset.spi.SyncProvider; -import static org.testng.Assert.*; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.PropertyStubProvider; import util.StubSyncProvider; import util.StubContext; @@ -67,7 +69,7 @@ public class SyncFactoryTests { ctx = new StubContext(); } - @BeforeMethod + @BeforeEach public void setUpMethod() throws Exception { // Make sure the provider provider that is registered is removed // before each run @@ -120,17 +122,21 @@ public class SyncFactoryTests { /* * Validate that a SyncFactoryException is thrown if the ProviderID is null */ - @Test(expectedExceptions = SyncFactoryException.class) + @Test public void test03() throws SyncFactoryException { - SyncProvider p = SyncFactory.getInstance(null); + assertThrows(SyncFactoryException.class, () -> { + SyncProvider p = SyncFactory.getInstance(null); + }); } /* * Validate that a SyncFactoryException is thrown if the Logger is null */ - @Test(expectedExceptions = SyncFactoryException.class,enabled=true) + @Test public void test04() throws SyncFactoryException { - Logger l = SyncFactory.getLogger(); + assertThrows(SyncFactoryException.class, () -> { + Logger l = SyncFactory.getLogger(); + }); } /* @@ -179,15 +185,15 @@ public class SyncFactoryTests { * Validate that setJNDIContext throws a SyncFactoryException if the * context is null */ - @Test(expectedExceptions = SyncFactoryException.class, enabled=true) + @Test public void test08() throws Exception { - SyncFactory.setJNDIContext(null); + assertThrows(SyncFactoryException.class, () -> SyncFactory.setJNDIContext(null)); } /* * Validate that setJNDIContext succeeds */ - @Test(enabled=true) + @Test public void test09() throws Exception { SyncFactory.setJNDIContext(ctx); } diff --git a/test/jdk/javax/sql/testng/test/rowset/spi/SyncProviderExceptionTests.java b/test/jdk/javax/sql/test/rowset/spi/SyncProviderExceptionTests.java similarity index 96% rename from test/jdk/javax/sql/testng/test/rowset/spi/SyncProviderExceptionTests.java rename to test/jdk/javax/sql/test/rowset/spi/SyncProviderExceptionTests.java index 22863b0f6bb..f8786f1b429 100644 --- a/test/jdk/javax/sql/testng/test/rowset/spi/SyncProviderExceptionTests.java +++ b/test/jdk/javax/sql/test/rowset/spi/SyncProviderExceptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, 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 @@ -27,11 +27,12 @@ import java.sql.SQLException; import javax.sql.rowset.spi.SyncProviderException; import javax.sql.rowset.spi.SyncResolver; -import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import util.BaseTest; import util.StubSyncResolver; @@ -40,16 +41,16 @@ public class SyncProviderExceptionTests extends BaseTest { // Used by SyncProviderException::getSyncResolver tests private SyncResolver resolver; - @BeforeClass + @BeforeAll public static void setUpClass() throws Exception { System.out.println(System.getProperty("java.naming.factory.initial")); } - @AfterClass + @AfterAll public static void tearDownClass() throws Exception { } - @BeforeMethod + @BeforeEach public void setupTest() { resolver = new SyncProviderException().getSyncResolver(); } diff --git a/test/jdk/javax/sql/testng/test/rowset/webrowset/CommonWebRowSetTests.java b/test/jdk/javax/sql/test/rowset/webrowset/CommonWebRowSetTests.java similarity index 81% rename from test/jdk/javax/sql/testng/test/rowset/webrowset/CommonWebRowSetTests.java rename to test/jdk/javax/sql/test/rowset/webrowset/CommonWebRowSetTests.java index ce0801e0fe4..023adc8254b 100644 --- a/test/jdk/javax/sql/testng/test/rowset/webrowset/CommonWebRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/webrowset/CommonWebRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 @@ -35,11 +35,15 @@ import java.math.BigDecimal; import java.sql.ResultSet; import java.util.Arrays; import javax.sql.rowset.WebRowSet; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertEqualsNoOrder; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; -import org.testng.annotations.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import test.rowset.cachedrowset.CommonCachedRowSetTests; public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { @@ -131,10 +135,11 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { /* * Validate the expected Rows are contained within the RowSet */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0000(WebRowSet wrs) throws Exception { - assertEquals(getPrimaryKeys(wrs), COFFEES_PRIMARY_KEYS); - assertEquals(wrs.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs)); + assertEquals(COFFEES_ROWS, wrs.size()); wrs.close(); } @@ -142,14 +147,15 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate the expected Rows are contained within the RowSet * populated by readXML(Reader) */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0001(WebRowSet wrs1) throws Exception { try (FileReader fr = new FileReader(COFFEE_ROWS_XML)) { wrs1.readXml(fr); } - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); wrs1.close(); } @@ -158,13 +164,14 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate the expected Rows are contained within the RowSet * populated by readXML(InputStream) */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0002(WebRowSet wrs1) throws Exception { try (FileInputStream fis = new FileInputStream(COFFEE_ROWS_XML)) { wrs1.readXml(fis); } - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); wrs1.close(); } @@ -173,12 +180,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * back via readXML(InputStream) and validate the primary keys * are the same */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0003(WebRowSet wrs) throws Exception { ByteArrayOutputStream baos = writeWebRowSetWithOutputStream(wrs); try (WebRowSet wrs1 = readWebRowSetWithOInputStream(baos)) { - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); } } @@ -187,14 +195,15 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * back via readXML(InputStream) and validate the primary keys * are the same */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0004(WebRowSet wrs) throws Exception { ResultSet rs = wrs; rs.beforeFirst(); ByteArrayOutputStream baos = writeWebRowSetWithOutputStream(rs); try (WebRowSet wrs1 = readWebRowSetWithOInputStream(baos)) { - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); } } @@ -203,12 +212,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * back via readXML(Reader) and validate the primary keys * are the same */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0005(WebRowSet wrs) throws Exception { ByteArrayOutputStream baos = writeWebRowSetWithOutputStreamWithWriter(wrs); try (WebRowSet wrs1 = readWebRowSetWithOInputStreamWithReader(baos)) { - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); } } @@ -217,14 +227,15 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * back via readXML(Reader) and validate the primary keys * are the same */ - @Test(dataProvider = "rowsetUsingCoffees") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0006(WebRowSet wrs) throws Exception { ResultSet rs = wrs; rs.beforeFirst(); ByteArrayOutputStream baos = writeWebRowSetWithOutputStreamWithWriter(rs); try (WebRowSet wrs1 = readWebRowSetWithOInputStreamWithReader(baos)) { - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); - assertEquals(wrs1.size(), COFFEES_ROWS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); + assertEquals(COFFEES_ROWS, wrs1.size()); } } @@ -232,11 +243,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate the expected Rows are contained within the RowSet * after deleting the specified rows */ - @Test(dataProvider = "rowsetUsingCoffees", enabled = false) + @Disabled + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowsetUsingCoffees") public void WebRowSetTest0007(WebRowSet wrs) throws Exception { - assertEquals(getPrimaryKeys(wrs), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs)); int[] rowsToDelete = {2, 4}; - assertEquals(getPrimaryKeys(wrs), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs)); for (int row : rowsToDelete) { assertTrue(deleteRowByPrimaryKey(wrs, row, 1)); } @@ -262,12 +275,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * that was populated by reading an xml file with all rows * marked as a currentRow */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0008(WebRowSet wrs1) throws Exception { FileInputStream fis = new FileInputStream(COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == COFFEES_ROWS); - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); // Validate that the rows are not marked as deleted, inserted or updated wrs1.beforeFirst(); while (wrs1.next()) { @@ -284,14 +298,15 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Also validate that they are or are not visible based on the * setShowDeleted value */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0009(WebRowSet wrs1) throws Exception { int[] rowsToDelete = {2, 4}; Object[] expectedRows = {1, 3, 5}; FileInputStream fis = new FileInputStream(DELETED_COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == COFFEES_ROWS); - assertEquals(getPrimaryKeys(wrs1), expectedRows); + assertArrayEquals(expectedRows, getPrimaryKeys(wrs1)); // With setShowDeleted(false) which is the default, // the deleted row should not be visible for (int row : rowsToDelete) { @@ -302,7 +317,7 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { for (int row : rowsToDelete) { assertTrue(findRowByPrimaryKey(wrs1, row, 1)); } - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); wrs1.close(); } @@ -311,12 +326,13 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate that the correct row in the WebRowSet that had been created * from an xml file is marked as updated and contains the correct values */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0010(WebRowSet wrs1) throws Exception { FileInputStream fis = new FileInputStream(UPDATED_COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == COFFEES_ROWS); - assertEquals(getPrimaryKeys(wrs1), COFFEES_PRIMARY_KEYS); + assertArrayEquals(COFFEES_PRIMARY_KEYS, getPrimaryKeys(wrs1)); wrs1.beforeFirst(); while (wrs1.next()) { if (wrs1.getInt(1) == 3) { @@ -337,7 +353,8 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { * Validate the correct row is marked as inserted in a WebRowSet * that is read from an xml file */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0011(WebRowSet wrs1) throws Exception { int expectedSize = COFFEES_ROWS + 2; int addedRowPK = 15; @@ -348,7 +365,10 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { FileInputStream fis = new FileInputStream(INSERTED_COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == expectedSize); - assertEqualsNoOrder(getPrimaryKeys(wrs1), expected); + var actual = getPrimaryKeys(wrs1); + Arrays.sort(actual); + Arrays.sort(expected); + assertArrayEquals(expected, actual); wrs1.beforeFirst(); while (wrs1.next()) { if (wrs1.getInt(1) == 15 || wrs1.getInt(1) == 20) { @@ -367,7 +387,8 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { /* * Read an xml file which contains a row that was inserted and updated */ - @Test(dataProvider = "rowSetType") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("rowSetType") public void WebRowSetTest0012(WebRowSet wrs1) throws Exception { int expectedSize = COFFEES_ROWS + 1; int addedRowPK = 100; @@ -376,7 +397,7 @@ public abstract class CommonWebRowSetTests extends CommonCachedRowSetTests { FileInputStream fis = new FileInputStream(UPDATED_INSERTED_COFFEE_ROWS_XML); wrs1.readXml(fis); assertTrue(wrs1.size() == expectedSize); - assertEquals(getPrimaryKeys(wrs1), expected); + assertArrayEquals(expected, getPrimaryKeys(wrs1)); wrs1.beforeFirst(); while (wrs1.next()) { if (wrs1.getInt(1) == addedRowPK) { diff --git a/test/jdk/javax/sql/testng/test/rowset/webrowset/WebRowSetTests.java b/test/jdk/javax/sql/test/rowset/webrowset/WebRowSetTests.java similarity index 94% rename from test/jdk/javax/sql/testng/test/rowset/webrowset/WebRowSetTests.java rename to test/jdk/javax/sql/test/rowset/webrowset/WebRowSetTests.java index 5f2890e0b5b..4692c0cee84 100644 --- a/test/jdk/javax/sql/testng/test/rowset/webrowset/WebRowSetTests.java +++ b/test/jdk/javax/sql/test/rowset/webrowset/WebRowSetTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 diff --git a/test/jdk/javax/sql/testng/util/PropertyStubProvider.java b/test/jdk/javax/sql/util/PropertyStubProvider.java similarity index 92% rename from test/jdk/javax/sql/testng/util/PropertyStubProvider.java rename to test/jdk/javax/sql/util/PropertyStubProvider.java index d397cc1c7d4..1cadcef26bb 100644 --- a/test/jdk/javax/sql/testng/util/PropertyStubProvider.java +++ b/test/jdk/javax/sql/util/PropertyStubProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubArray.java b/test/jdk/javax/sql/util/StubArray.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubArray.java rename to test/jdk/javax/sql/util/StubArray.java index 1d2ef0e5b6f..b57760679a0 100644 --- a/test/jdk/javax/sql/testng/util/StubArray.java +++ b/test/jdk/javax/sql/util/StubArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubBaseRowSet.java b/test/jdk/javax/sql/util/StubBaseRowSet.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubBaseRowSet.java rename to test/jdk/javax/sql/util/StubBaseRowSet.java index e003b522ed9..94be7f8e348 100644 --- a/test/jdk/javax/sql/testng/util/StubBaseRowSet.java +++ b/test/jdk/javax/sql/util/StubBaseRowSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubBlob.java b/test/jdk/javax/sql/util/StubBlob.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubBlob.java rename to test/jdk/javax/sql/util/StubBlob.java index 727e8a926d5..f86fd540841 100644 --- a/test/jdk/javax/sql/testng/util/StubBlob.java +++ b/test/jdk/javax/sql/util/StubBlob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubCachedRowSetImpl.java b/test/jdk/javax/sql/util/StubCachedRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubCachedRowSetImpl.java rename to test/jdk/javax/sql/util/StubCachedRowSetImpl.java index 9000deb1b65..65e8169dbb6 100644 --- a/test/jdk/javax/sql/testng/util/StubCachedRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubCachedRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubClob.java b/test/jdk/javax/sql/util/StubClob.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubClob.java rename to test/jdk/javax/sql/util/StubClob.java index cb1e0c0a62a..b0db14b3526 100644 --- a/test/jdk/javax/sql/testng/util/StubClob.java +++ b/test/jdk/javax/sql/util/StubClob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubContext.java b/test/jdk/javax/sql/util/StubContext.java similarity index 98% rename from test/jdk/javax/sql/testng/util/StubContext.java rename to test/jdk/javax/sql/util/StubContext.java index af03534d991..6f03a1abd48 100644 --- a/test/jdk/javax/sql/testng/util/StubContext.java +++ b/test/jdk/javax/sql/util/StubContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubFilteredRowSetImpl.java b/test/jdk/javax/sql/util/StubFilteredRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubFilteredRowSetImpl.java rename to test/jdk/javax/sql/util/StubFilteredRowSetImpl.java index 8fcbfa4c085..0b73abc6c01 100644 --- a/test/jdk/javax/sql/testng/util/StubFilteredRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubFilteredRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubJdbcRowSetImpl.java b/test/jdk/javax/sql/util/StubJdbcRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubJdbcRowSetImpl.java rename to test/jdk/javax/sql/util/StubJdbcRowSetImpl.java index ec59eb6c556..441b662ddc6 100644 --- a/test/jdk/javax/sql/testng/util/StubJdbcRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubJdbcRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubJoinRowSetImpl.java b/test/jdk/javax/sql/util/StubJoinRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubJoinRowSetImpl.java rename to test/jdk/javax/sql/util/StubJoinRowSetImpl.java index c7ece7cd523..1f506f830ef 100644 --- a/test/jdk/javax/sql/testng/util/StubJoinRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubJoinRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubNClob.java b/test/jdk/javax/sql/util/StubNClob.java similarity index 93% rename from test/jdk/javax/sql/testng/util/StubNClob.java rename to test/jdk/javax/sql/util/StubNClob.java index 10682578b9a..0664aa42795 100644 --- a/test/jdk/javax/sql/testng/util/StubNClob.java +++ b/test/jdk/javax/sql/util/StubNClob.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubRef.java b/test/jdk/javax/sql/util/StubRef.java similarity index 95% rename from test/jdk/javax/sql/testng/util/StubRef.java rename to test/jdk/javax/sql/util/StubRef.java index 052bd92c740..54a42df1e1c 100644 --- a/test/jdk/javax/sql/testng/util/StubRef.java +++ b/test/jdk/javax/sql/util/StubRef.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubRowId.java b/test/jdk/javax/sql/util/StubRowId.java similarity index 93% rename from test/jdk/javax/sql/testng/util/StubRowId.java rename to test/jdk/javax/sql/util/StubRowId.java index f8e90d49867..d3bc51b9320 100644 --- a/test/jdk/javax/sql/testng/util/StubRowId.java +++ b/test/jdk/javax/sql/util/StubRowId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubRowSetFactory.java b/test/jdk/javax/sql/util/StubRowSetFactory.java similarity index 96% rename from test/jdk/javax/sql/testng/util/StubRowSetFactory.java rename to test/jdk/javax/sql/util/StubRowSetFactory.java index 624c53af1e7..abc34894a18 100644 --- a/test/jdk/javax/sql/testng/util/StubRowSetFactory.java +++ b/test/jdk/javax/sql/util/StubRowSetFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubSQLXML.java b/test/jdk/javax/sql/util/StubSQLXML.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubSQLXML.java rename to test/jdk/javax/sql/util/StubSQLXML.java index 44d1351b357..5e4d1647853 100644 --- a/test/jdk/javax/sql/testng/util/StubSQLXML.java +++ b/test/jdk/javax/sql/util/StubSQLXML.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubStruct.java b/test/jdk/javax/sql/util/StubStruct.java similarity index 95% rename from test/jdk/javax/sql/testng/util/StubStruct.java rename to test/jdk/javax/sql/util/StubStruct.java index 1dee8028a2c..a5a2004a09d 100644 --- a/test/jdk/javax/sql/testng/util/StubStruct.java +++ b/test/jdk/javax/sql/util/StubStruct.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubSyncProvider.java b/test/jdk/javax/sql/util/StubSyncProvider.java similarity index 97% rename from test/jdk/javax/sql/testng/util/StubSyncProvider.java rename to test/jdk/javax/sql/util/StubSyncProvider.java index 8947455c701..a6a44e0f509 100644 --- a/test/jdk/javax/sql/testng/util/StubSyncProvider.java +++ b/test/jdk/javax/sql/util/StubSyncProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, 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 diff --git a/test/jdk/javax/sql/testng/util/StubSyncResolver.java b/test/jdk/javax/sql/util/StubSyncResolver.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubSyncResolver.java rename to test/jdk/javax/sql/util/StubSyncResolver.java index 02477c7f9be..f24c3ec4b13 100644 --- a/test/jdk/javax/sql/testng/util/StubSyncResolver.java +++ b/test/jdk/javax/sql/util/StubSyncResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/StubWebRowSetImpl.java b/test/jdk/javax/sql/util/StubWebRowSetImpl.java similarity index 99% rename from test/jdk/javax/sql/testng/util/StubWebRowSetImpl.java rename to test/jdk/javax/sql/util/StubWebRowSetImpl.java index 918d5bd0482..48c237e73ae 100644 --- a/test/jdk/javax/sql/testng/util/StubWebRowSetImpl.java +++ b/test/jdk/javax/sql/util/StubWebRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/SuperHero.java b/test/jdk/javax/sql/util/SuperHero.java similarity index 97% rename from test/jdk/javax/sql/testng/util/SuperHero.java rename to test/jdk/javax/sql/util/SuperHero.java index c8afb3831de..2d6e68a7c9e 100644 --- a/test/jdk/javax/sql/testng/util/SuperHero.java +++ b/test/jdk/javax/sql/util/SuperHero.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/TestRowSetListener.java b/test/jdk/javax/sql/util/TestRowSetListener.java similarity index 96% rename from test/jdk/javax/sql/testng/util/TestRowSetListener.java rename to test/jdk/javax/sql/util/TestRowSetListener.java index 069ade29ca2..4f71f12da2e 100644 --- a/test/jdk/javax/sql/testng/util/TestRowSetListener.java +++ b/test/jdk/javax/sql/util/TestRowSetListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/util/TestSQLDataImpl.java b/test/jdk/javax/sql/util/TestSQLDataImpl.java similarity index 98% rename from test/jdk/javax/sql/testng/util/TestSQLDataImpl.java rename to test/jdk/javax/sql/util/TestSQLDataImpl.java index 0694add3ef9..f02eeb6fece 100644 --- a/test/jdk/javax/sql/testng/util/TestSQLDataImpl.java +++ b/test/jdk/javax/sql/util/TestSQLDataImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 diff --git a/test/jdk/javax/sql/testng/xml/COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/DELETED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/DELETED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/DELETED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/DELETED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/INSERTED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/INSERTED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/INSERTED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/INSERTED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/MODFIED_DELETED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/MODFIED_DELETED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/MODFIED_DELETED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/MODFIED_DELETED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/UPDATED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/UPDATED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/UPDATED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/UPDATED_COFFEE_ROWS.xml diff --git a/test/jdk/javax/sql/testng/xml/UPDATED_INSERTED_COFFEE_ROWS.xml b/test/jdk/javax/sql/xml/UPDATED_INSERTED_COFFEE_ROWS.xml similarity index 100% rename from test/jdk/javax/sql/testng/xml/UPDATED_INSERTED_COFFEE_ROWS.xml rename to test/jdk/javax/sql/xml/UPDATED_INSERTED_COFFEE_ROWS.xml From 3a4277db74f889d0b8350145515c1a1f4e399ec8 Mon Sep 17 00:00:00 2001 From: Srinivas Vamsi Parasa Date: Fri, 30 Jan 2026 17:50:58 +0000 Subject: [PATCH 065/215] =?UTF-8?q?8374744:=20Enable=20dumping=20of=20APX?= =?UTF-8?q?=20EGPRs=20(R16=E2=80=93R31)=20in=20JVM=20fatal=20error=20logs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: sviswanathan, dholmes --- src/hotspot/cpu/x86/vm_version_x86.cpp | 16 ++- src/hotspot/cpu/x86/vm_version_x86.hpp | 9 ++ src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp | 101 ++++++++++++++---- 3 files changed, 104 insertions(+), 22 deletions(-) diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 0fff5b44e00..74df41f8682 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -143,7 +143,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator { Label detect_486, cpu486, detect_586, std_cpuid1, std_cpuid4, std_cpuid24, std_cpuid29; Label sef_cpuid, sefsl1_cpuid, ext_cpuid, ext_cpuid1, ext_cpuid5, ext_cpuid7; - Label ext_cpuid8, done, wrapup, vector_save_restore, apx_save_restore_warning; + Label ext_cpuid8, done, wrapup, vector_save_restore, apx_save_restore_warning, apx_xstate; Label legacy_setup, save_restore_except, legacy_save_restore, start_simd_check; StubCodeMark mark(this, "VM_Version", "get_cpu_info_stub"); @@ -468,6 +468,20 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movq(Address(rsi, 0), r16); __ movq(Address(rsi, 8), r31); + // + // Query CPUID 0xD.19 for APX XSAVE offset + // Extended State Enumeration Sub-leaf 19 (APX) + // EAX = size of APX state (should be 128) + // EBX = offset in standard XSAVE format + // + __ movl(rax, 0xD); + __ movl(rcx, 19); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::apx_xstate_size_offset()))); + __ movl(Address(rsi, 0), rax); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::apx_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + UseAPX = save_apx; __ bind(vector_save_restore); // diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index cc93ee3564e..a3f2a801198 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -676,6 +676,10 @@ protected: // Space to save apx registers after signal handle jlong apx_save[2]; // Save r16 and r31 + // cpuid function 0xD, subleaf 19 (APX extended state) + uint32_t apx_xstate_size; // EAX: size of APX state (128) + uint32_t apx_xstate_offset; // EBX: offset in standard XSAVE area + VM_Features feature_flags() const; // Asserts @@ -739,6 +743,11 @@ public: static ByteSize ymm_save_offset() { return byte_offset_of(CpuidInfo, ymm_save); } static ByteSize zmm_save_offset() { return byte_offset_of(CpuidInfo, zmm_save); } static ByteSize apx_save_offset() { return byte_offset_of(CpuidInfo, apx_save); } + static ByteSize apx_xstate_offset_offset() { return byte_offset_of(CpuidInfo, apx_xstate_offset); } + static ByteSize apx_xstate_size_offset() { return byte_offset_of(CpuidInfo, apx_xstate_size); } + + static uint32_t apx_xstate_offset() { return _cpuid_info.apx_xstate_offset; } + static uint32_t apx_xstate_size() { return _cpuid_info.apx_xstate_size; } // The value used to check ymm register after signal handle static int ymm_test_value() { return 0xCAFEBABE; } diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 07f53582a76..ee08738c678 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -52,6 +52,7 @@ #include "utilities/debug.hpp" #include "utilities/events.hpp" #include "utilities/vmError.hpp" +#include "runtime/vm_version.hpp" // put OS-includes here # include @@ -380,6 +381,43 @@ size_t os::Posix::default_stack_size(os::ThreadType thr_type) { ///////////////////////////////////////////////////////////////////////////// // helper functions for fatal error handler +// XSAVE constants - from Intel SDM Vol. 1, Chapter 13 +#define XSAVE_HDR_OFFSET 512 +#define XFEATURE_APX (1ULL << 19) + +// XSAVE header structure +// See: Intel SDM Vol. 1, Section 13.4.2 "XSAVE Header" +// Also: Linux kernel arch/x86/include/asm/fpu/types.h +struct xstate_header { + uint64_t xfeatures; + uint64_t xcomp_bv; + uint64_t reserved[6]; +}; + +// APX extended state - R16-R31 (16 x 64-bit registers) +// See: Intel APX Architecture Specification +struct apx_state { + uint64_t regs[16]; // r16-r31 +}; + +static apx_state* get_apx_state(const ucontext_t* uc) { + uint32_t offset = VM_Version::apx_xstate_offset(); + if (offset == 0 || uc->uc_mcontext.fpregs == nullptr) { + return nullptr; + } + + char* xsave = (char*)uc->uc_mcontext.fpregs; + xstate_header* hdr = (xstate_header*)(xsave + XSAVE_HDR_OFFSET); + + // Check if APX state is present in this context + if (!(hdr->xfeatures & XFEATURE_APX)) { + return nullptr; + } + + return (apx_state*)(xsave + offset); +} + + void os::print_context(outputStream *st, const void *context) { if (context == nullptr) return; @@ -406,6 +444,14 @@ void os::print_context(outputStream *st, const void *context) { st->print(", R14=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_R14]); st->print(", R15=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_R15]); st->cr(); + // Dump APX EGPRs (R16-R31) + apx_state* apx = UseAPX ? get_apx_state(uc) : nullptr; + if (apx != nullptr) { + for (int i = 0; i < 16; i++) { + st->print("%sR%d=" INTPTR_FORMAT, (i % 4 == 0) ? "" : ", ", 16 + i, (intptr_t)apx->regs[i]); + if (i % 4 == 3) st->cr(); + } + } st->print( "RIP=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_RIP]); st->print(", EFLAGS=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_EFL]); st->print(", CSGSFS=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_CSGSFS]); @@ -432,37 +478,50 @@ void os::print_context(outputStream *st, const void *context) { } void os::print_register_info(outputStream *st, const void *context, int& continuation) { - const int register_count = 16; + if (context == nullptr) { + return; + } + const ucontext_t *uc = (const ucontext_t*)context; + apx_state* apx = UseAPX ? get_apx_state(uc) : nullptr; + + const int register_count = 16 + (apx != nullptr ? 16 : 0); int n = continuation; assert(n >= 0 && n <= register_count, "Invalid continuation value"); - if (context == nullptr || n == register_count) { + if (n == register_count) { return; } - const ucontext_t *uc = (const ucontext_t*)context; while (n < register_count) { // Update continuation with next index before printing location continuation = n + 1; + + if (n < 16) { + // Standard registers (RAX-R15) # define CASE_PRINT_REG(n, str, id) case n: st->print(str); print_location(st, uc->uc_mcontext.gregs[REG_##id]); - switch (n) { - CASE_PRINT_REG( 0, "RAX=", RAX); break; - CASE_PRINT_REG( 1, "RBX=", RBX); break; - CASE_PRINT_REG( 2, "RCX=", RCX); break; - CASE_PRINT_REG( 3, "RDX=", RDX); break; - CASE_PRINT_REG( 4, "RSP=", RSP); break; - CASE_PRINT_REG( 5, "RBP=", RBP); break; - CASE_PRINT_REG( 6, "RSI=", RSI); break; - CASE_PRINT_REG( 7, "RDI=", RDI); break; - CASE_PRINT_REG( 8, "R8 =", R8); break; - CASE_PRINT_REG( 9, "R9 =", R9); break; - CASE_PRINT_REG(10, "R10=", R10); break; - CASE_PRINT_REG(11, "R11=", R11); break; - CASE_PRINT_REG(12, "R12=", R12); break; - CASE_PRINT_REG(13, "R13=", R13); break; - CASE_PRINT_REG(14, "R14=", R14); break; - CASE_PRINT_REG(15, "R15=", R15); break; - } + switch (n) { + CASE_PRINT_REG( 0, "RAX=", RAX); break; + CASE_PRINT_REG( 1, "RBX=", RBX); break; + CASE_PRINT_REG( 2, "RCX=", RCX); break; + CASE_PRINT_REG( 3, "RDX=", RDX); break; + CASE_PRINT_REG( 4, "RSP=", RSP); break; + CASE_PRINT_REG( 5, "RBP=", RBP); break; + CASE_PRINT_REG( 6, "RSI=", RSI); break; + CASE_PRINT_REG( 7, "RDI=", RDI); break; + CASE_PRINT_REG( 8, "R8 =", R8); break; + CASE_PRINT_REG( 9, "R9 =", R9); break; + CASE_PRINT_REG(10, "R10=", R10); break; + CASE_PRINT_REG(11, "R11=", R11); break; + CASE_PRINT_REG(12, "R12=", R12); break; + CASE_PRINT_REG(13, "R13=", R13); break; + CASE_PRINT_REG(14, "R14=", R14); break; + CASE_PRINT_REG(15, "R15=", R15); break; + } # undef CASE_PRINT_REG + } else { + // APX extended general purpose registers (R16-R31) + st->print("R%d=", n); + print_location(st, apx->regs[n - 16]); + } ++n; } } From 32e00ff33785f0756cb320cd8c0ffad8eda76153 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 30 Jan 2026 19:07:59 +0000 Subject: [PATCH 066/215] 8375567: Remove AppContext usage from Swing Motif L&F classes Reviewed-by: serb, psadhukhan --- .../sun/java/swing/plaf/motif/MotifButtonUI.java | 13 ++----------- .../sun/java/swing/plaf/motif/MotifCheckBoxUI.java | 13 ++----------- .../com/sun/java/swing/plaf/motif/MotifLabelUI.java | 13 ++----------- .../java/swing/plaf/motif/MotifToggleButtonUI.java | 13 ++----------- 4 files changed, 8 insertions(+), 44 deletions(-) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java index 0e4c2e526ac..7a11e4a47e9 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java @@ -42,8 +42,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonUI; -import sun.awt.AppContext; - /** * MotifButton implementation * @@ -55,20 +53,13 @@ public class MotifButtonUI extends BasicButtonUI { private boolean defaults_initialized = false; - private static final Object MOTIF_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new MotifButtonUI(); // ******************************** // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - MotifButtonUI motifButtonUI = - (MotifButtonUI) appContext.get(MOTIF_BUTTON_UI_KEY); - if (motifButtonUI == null) { - motifButtonUI = new MotifButtonUI(); - appContext.put(MOTIF_BUTTON_UI_KEY, motifButtonUI); - } - return motifButtonUI; + return UI; } // ******************************** diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java index 9e9f0008b07..7017eaf546c 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java @@ -30,8 +30,6 @@ import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; -import sun.awt.AppContext; - /** * MotifCheckBox implementation * @@ -39,7 +37,7 @@ import sun.awt.AppContext; */ public class MotifCheckBoxUI extends MotifRadioButtonUI { - private static final Object MOTIF_CHECK_BOX_UI_KEY = new Object(); + private static final ComponentUI UI = new MotifCheckBoxUI(); private static final String propertyPrefix = "CheckBox" + "."; @@ -50,14 +48,7 @@ public class MotifCheckBoxUI extends MotifRadioButtonUI { // Create PLAF // ******************************** public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - MotifCheckBoxUI motifCheckBoxUI = - (MotifCheckBoxUI) appContext.get(MOTIF_CHECK_BOX_UI_KEY); - if (motifCheckBoxUI == null) { - motifCheckBoxUI = new MotifCheckBoxUI(); - appContext.put(MOTIF_CHECK_BOX_UI_KEY, motifCheckBoxUI); - } - return motifCheckBoxUI; + return UI; } @Override diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java index f19a3c23cb5..89c2e309f9e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java @@ -29,8 +29,6 @@ import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicLabelUI; -import sun.awt.AppContext; - /** * A Motif {@literal L&F} implementation of LabelUI. * This merely sets up new default values in MotifLookAndFeel. @@ -39,16 +37,9 @@ import sun.awt.AppContext; */ public class MotifLabelUI extends BasicLabelUI { - private static final Object MOTIF_LABEL_UI_KEY = new Object(); + private static final ComponentUI UI = new MotifLabelUI(); public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - MotifLabelUI motifLabelUI = - (MotifLabelUI) appContext.get(MOTIF_LABEL_UI_KEY); - if (motifLabelUI == null) { - motifLabelUI = new MotifLabelUI(); - appContext.put(MOTIF_LABEL_UI_KEY, motifLabelUI); - } - return motifLabelUI; + return UI; } } diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java index 7997145e648..7956023ed06 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java @@ -39,8 +39,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicToggleButtonUI; -import sun.awt.AppContext; - /** * BasicToggleButton implementation. * @@ -48,7 +46,7 @@ import sun.awt.AppContext; */ public class MotifToggleButtonUI extends BasicToggleButtonUI { - private static final Object MOTIF_TOGGLE_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new MotifToggleButtonUI(); protected Color selectColor; @@ -58,14 +56,7 @@ public class MotifToggleButtonUI extends BasicToggleButtonUI // Create PLAF // ******************************** public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - MotifToggleButtonUI motifToggleButtonUI = - (MotifToggleButtonUI) appContext.get(MOTIF_TOGGLE_BUTTON_UI_KEY); - if (motifToggleButtonUI == null) { - motifToggleButtonUI = new MotifToggleButtonUI(); - appContext.put(MOTIF_TOGGLE_BUTTON_UI_KEY, motifToggleButtonUI); - } - return motifToggleButtonUI; + return UI; } // ******************************** From 9ef98a5fb194eec3024b87ea9f9c9acee952dcf6 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 30 Jan 2026 19:08:20 +0000 Subject: [PATCH 067/215] 8376747: Remove AppContext from Swing LayoutStyle Reviewed-by: psadhukhan, azvegint --- .../share/classes/javax/swing/LayoutStyle.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/LayoutStyle.java b/src/java.desktop/share/classes/javax/swing/LayoutStyle.java index f1c384db6e2..bf84eb2379b 100644 --- a/src/java.desktop/share/classes/javax/swing/LayoutStyle.java +++ b/src/java.desktop/share/classes/javax/swing/LayoutStyle.java @@ -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 @@ -26,7 +26,6 @@ package javax.swing; import java.awt.Container; import javax.swing.plaf.ComponentUI; -import sun.awt.AppContext; /** * LayoutStyle provides information about how to position @@ -40,6 +39,9 @@ import sun.awt.AppContext; * @since 1.6 */ public abstract class LayoutStyle { + + private static LayoutStyle sharedInstance; + /** * Sets the shared instance of LayoutStyle. Specifying * null results in using the LayoutStyle from @@ -51,10 +53,10 @@ public abstract class LayoutStyle { public static void setInstance(LayoutStyle style) { synchronized(LayoutStyle.class) { if (style == null) { - AppContext.getAppContext().remove(LayoutStyle.class); + sharedInstance = null; } else { - AppContext.getAppContext().put(LayoutStyle.class, style); + sharedInstance = style; } } } @@ -70,8 +72,7 @@ public abstract class LayoutStyle { public static LayoutStyle getInstance() { LayoutStyle style; synchronized(LayoutStyle.class) { - style = (LayoutStyle)AppContext.getAppContext(). - get(LayoutStyle.class); + style = sharedInstance; } if (style == null) { return UIManager.getLookAndFeel().getLayoutStyle(); From c62c82d5e0485b8570bb1c61805e518fe05f3ec4 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Fri, 30 Jan 2026 19:27:45 +0000 Subject: [PATCH 068/215] 8376420: Remove AppContext from javax/swing/ImageIcon.java Reviewed-by: aivanov, psadhukhan --- .../share/classes/javax/swing/ImageIcon.java | 36 +++---------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/ImageIcon.java b/src/java.desktop/share/classes/javax/swing/ImageIcon.java index aaaa2dfd4a4..ee6c08ebb15 100644 --- a/src/java.desktop/share/classes/javax/swing/ImageIcon.java +++ b/src/java.desktop/share/classes/javax/swing/ImageIcon.java @@ -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 @@ -54,7 +54,6 @@ import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; import sun.awt.AWTAccessor; -import sun.awt.AppContext; /** * An implementation of the Icon interface that paints Icons @@ -110,18 +109,7 @@ public class ImageIcon implements Icon, Serializable, Accessible { * @deprecated since 1.8 */ @Deprecated - protected static final Component component = createComponent(); - - private static final Component createComponent() { - try { - Component component = new Component() {}; - // 6482575 - clear the appContext field so as not to leak it - AWTAccessor.getComponentAccessor().setAppContext(component, null); - return component; - } catch (Throwable t) { - return null; - } - } + protected static final Component component = new Component() {}; /** * Do not use this shared media tracker, which is used to load images. @@ -136,8 +124,6 @@ public class ImageIcon implements Icon, Serializable, Accessible { */ private static int mediaTrackerID; - private static final Object TRACKER_KEY = new StringBuilder("TRACKER_KEY"); - int width = -1; int height = -1; @@ -346,24 +332,12 @@ public class ImageIcon implements Icon, Serializable, Accessible { } } + private static final MediaTracker MEDIA_TRACKER = new MediaTracker(new Component() {}); /** - * Returns the MediaTracker for the current AppContext, creating a new - * MediaTracker if necessary. + * Returns the shared MediaTracker. */ private MediaTracker getTracker() { - Object trackerObj; - AppContext ac = AppContext.getAppContext(); - // Opt: Only synchronize if trackerObj comes back null? - // If null, synchronize, re-check for null, and put new tracker - synchronized(ac) { - trackerObj = ac.get(TRACKER_KEY); - if (trackerObj == null) { - Component comp = new Component() {}; - trackerObj = new MediaTracker(comp); - ac.put(TRACKER_KEY, trackerObj); - } - } - return (MediaTracker) trackerObj; + return MEDIA_TRACKER; } /** From 6ce2f3e18f31d1dbffc2c4f5adbb5dfe91613989 Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Fri, 30 Jan 2026 22:37:43 +0000 Subject: [PATCH 069/215] 8376751: add preview project anchors to main-line ProblemList files Reviewed-by: kvn, rriggs, liach --- make/RunTests.gmk | 6 ++- test/docs/ProblemList.txt | 15 ++++++- test/hotspot/jtreg/ProblemList-AotJdk.txt | 13 ++++++ test/hotspot/jtreg/ProblemList-StaticJdk.txt | 42 +++++++++++++++++++ test/hotspot/jtreg/ProblemList-Virtual.txt | 14 ++++++- test/hotspot/jtreg/ProblemList-Xcomp.txt | 15 ++++++- .../jtreg/ProblemList-enable-preview.txt | 6 ++- .../jtreg/ProblemList-jvmti-stress-agent.txt | 15 ++++++- test/hotspot/jtreg/ProblemList-zgc.txt | 15 ++++++- test/hotspot/jtreg/ProblemList.txt | 15 ++++++- test/jaxp/ProblemList.txt | 14 ++++++- test/jdk/ProblemList-AotJdk.txt | 14 ++++++- test/jdk/ProblemList-StaticJdk.txt | 15 ++++++- test/jdk/ProblemList-Virtual.txt | 14 ++++++- test/jdk/ProblemList-Xcomp.txt | 15 ++++++- test/jdk/ProblemList-coh.txt | 41 ++++++++++++++++++ test/jdk/ProblemList-enable-preview.txt | 6 ++- test/jdk/ProblemList-jvmti-stress-agent.txt | 15 ++++++- test/jdk/ProblemList-shenandoah.txt | 12 ++++++ test/jdk/ProblemList-zgc.txt | 15 ++++++- test/jdk/ProblemList.txt | 13 ++++++ test/langtools/ProblemList-StaticJdk.txt | 15 ++++++- test/langtools/ProblemList-enable-preview.txt | 32 ++++++++++++++ test/langtools/ProblemList.txt | 15 ++++++- test/lib-test/ProblemList-StaticJdk.txt | 15 ++++++- test/lib-test/ProblemList.txt | 15 ++++++- 26 files changed, 400 insertions(+), 22 deletions(-) create mode 100644 test/jdk/ProblemList-coh.txt create mode 100644 test/langtools/ProblemList-enable-preview.txt diff --git a/make/RunTests.gmk b/make/RunTests.gmk index 946b1332edc..02ea632e3ec 100644 --- a/make/RunTests.gmk +++ b/make/RunTests.gmk @@ -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 @@ -972,6 +972,10 @@ define SetupRunJtregTestBody JTREG_AUTO_PROBLEM_LISTS += ProblemList-enable-preview.txt endif + ifneq ($$(findstring -XX:+UseCompactObjectHeaders, $$(JTREG_ALL_OPTIONS)), ) + JTREG_AUTO_PROBLEM_LISTS += ProblemList-coh.txt + endif + ifneq ($$(JTREG_EXTRA_PROBLEM_LISTS), ) # Accept both absolute paths as well as relative to the current test root. diff --git a/test/docs/ProblemList.txt b/test/docs/ProblemList.txt index c846678665d..d856f9c955e 100644 --- a/test/docs/ProblemList.txt +++ b/test/docs/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# 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 @@ -39,3 +39,16 @@ # More than one label is allowed but must be on the same line. # ############################################################################# + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-AotJdk.txt b/test/hotspot/jtreg/ProblemList-AotJdk.txt index e27e85645f5..0a2177b8edb 100644 --- a/test/hotspot/jtreg/ProblemList-AotJdk.txt +++ b/test/hotspot/jtreg/ProblemList-AotJdk.txt @@ -56,3 +56,16 @@ compiler/ciReplay/TestInliningProtectionDomain.java 0000000 generic-all # These tests fail often with AotJdk due to JDK-8323727 compiler/arguments/TestStressReflectiveCode.java 8323727 generic-all compiler/arraycopy/TestCloneWithStressReflectiveCode.java 8323727 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-StaticJdk.txt b/test/hotspot/jtreg/ProblemList-StaticJdk.txt index 1e5538ca5b9..a6112169f8d 100644 --- a/test/hotspot/jtreg/ProblemList-StaticJdk.txt +++ b/test/hotspot/jtreg/ProblemList-StaticJdk.txt @@ -1,3 +1,32 @@ +# +# 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. +# + +############################################################################# +# +# List of quarantined tests for testing in StaticJdk mode. +# +############################################################################# + # Dynamically link with JDK/VM native libraries gtest/GTestWrapper.java 8356201 generic-all gtest/LargePageGtests.java#use-large-pages 8356201 generic-all @@ -6,3 +35,16 @@ gtest/MetaspaceGtests.java#no-ccs 8356201 generic-all gtest/NMTGtests.java#nmt-detail 8356201 generic-all gtest/NMTGtests.java#nmt-off 8356201 generic-all gtest/NMTGtests.java#nmt-summary 8356201 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 31684662194..7c4846a9fb2 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -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 @@ -77,3 +77,15 @@ vmTestbase/nsk/jdi/VMOutOfMemoryException/VMOutOfMemoryException001/VMOutOfMemor # to make progress when all other threads are currently suspended. vmTestbase/nsk/jdi/ThreadReference/isSuspended/issuspended002/TestDescription.java 8338713 generic-all +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-Xcomp.txt b/test/hotspot/jtreg/ProblemList-Xcomp.txt index 758bb32e968..e4f1774e6e0 100644 --- a/test/hotspot/jtreg/ProblemList-Xcomp.txt +++ b/test/hotspot/jtreg/ProblemList-Xcomp.txt @@ -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 @@ -46,3 +46,16 @@ vmTestbase/nsk/jvmti/scenarios/capability/CM03/cm03t001/TestDescription.java 829 vmTestbase/nsk/stress/thread/thread006.java 8321476 linux-all gc/arguments/TestNewSizeFlags.java 8299116 macosx-aarch64 + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-enable-preview.txt b/test/hotspot/jtreg/ProblemList-enable-preview.txt index 31e09d372a8..1a831a0dde3 100644 --- a/test/hotspot/jtreg/ProblemList-enable-preview.txt +++ b/test/hotspot/jtreg/ProblemList-enable-preview.txt @@ -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,6 +25,8 @@ # # List of quarantined tests for testing with --enable-preview # +# These are failures that ONLY occur with the '--enable-preview' option +# specified. There are separate sub-sections for each preview project. +# ############################################################################# - diff --git a/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt b/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt index eb4131691c9..1e84a39c30b 100644 --- a/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt +++ b/test/hotspot/jtreg/ProblemList-jvmti-stress-agent.txt @@ -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 @@ -96,3 +96,16 @@ serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorThreadTest.java serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorTwoAgentsTest.java 0000000 generic-all serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java#id0 0000000 generic-all serviceability/jvmti/HeapMonitor/MyPackage/HeapMonitorVMEventsTest.java#id1 0000000 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList-zgc.txt b/test/hotspot/jtreg/ProblemList-zgc.txt index 911e3adc9ca..3e52b7169df 100644 --- a/test/hotspot/jtreg/ProblemList-zgc.txt +++ b/test/hotspot/jtreg/ProblemList-zgc.txt @@ -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 @@ -113,3 +113,16 @@ compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/NativeCallTest.java compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleCodeInstallationTest.java 8343233 generic-aarch64 compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java 8343233 generic-aarch64 compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/VirtualObjectDebugInfoTest.java 8343233 generic-aarch64 + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 7e521279a4c..3e4814180f6 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -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 @@ -183,3 +183,16 @@ vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEa vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java 8076494 windows-x64 vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/TestDescription.java 8310144 macosx-aarch64 + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jaxp/ProblemList.txt b/test/jaxp/ProblemList.txt index 23ebb07f5ff..8f7380a43f8 100644 --- a/test/jaxp/ProblemList.txt +++ b/test/jaxp/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# Copyright (c) 2015, 2021, 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 @@ -23,3 +23,15 @@ # ########################################################################### +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-AotJdk.txt b/test/jdk/ProblemList-AotJdk.txt index 20960652dcf..c98bf63b25f 100644 --- a/test/jdk/ProblemList-AotJdk.txt +++ b/test/jdk/ProblemList-AotJdk.txt @@ -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 @@ -44,3 +44,15 @@ java/lang/invoke/DumpMethodHandleInternals.java 0000000 generic- # this message will not be printed in the production run. java/util/Locale/UseOldISOCodesTest.java 0000000 generic-all +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-StaticJdk.txt b/test/jdk/ProblemList-StaticJdk.txt index 70f0438c0c6..700099656e8 100644 --- a/test/jdk/ProblemList-StaticJdk.txt +++ b/test/jdk/ProblemList-StaticJdk.txt @@ -1,6 +1,6 @@ ########################################################################### # -# 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,3 +24,16 @@ ########################################################################### # Currently empty + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-Virtual.txt b/test/jdk/ProblemList-Virtual.txt index fc5f31e3ce8..52f187086de 100644 --- a/test/jdk/ProblemList-Virtual.txt +++ b/test/jdk/ProblemList-Virtual.txt @@ -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 @@ -34,3 +34,15 @@ java/lang/ScopedValue/StressStackOverflow.java#default 8309646 generic-all java/lang/ScopedValue/StressStackOverflow.java#no-TieredCompilation 8309646 generic-all java/lang/ScopedValue/StressStackOverflow.java#TieredStopAtLevel1 8309646 generic-all +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-Xcomp.txt b/test/jdk/ProblemList-Xcomp.txt index 5ed171a1fea..44bad411f26 100644 --- a/test/jdk/ProblemList-Xcomp.txt +++ b/test/jdk/ProblemList-Xcomp.txt @@ -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 @@ -29,3 +29,16 @@ java/lang/invoke/MethodHandles/CatchExceptionTest.java 8146623 generic-all java/lang/reflect/callerCache/ReflectionCallerCacheTest.java 8332028 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-coh.txt b/test/jdk/ProblemList-coh.txt new file mode 100644 index 00000000000..b3bddb0c9f4 --- /dev/null +++ b/test/jdk/ProblemList-coh.txt @@ -0,0 +1,41 @@ +# +# 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. +# + +############################################################################# +# +# List of quarantined tests for testing with -XX:+UseCompactObjectHeaders +# +############################################################################# + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-enable-preview.txt b/test/jdk/ProblemList-enable-preview.txt index 31e09d372a8..1a831a0dde3 100644 --- a/test/jdk/ProblemList-enable-preview.txt +++ b/test/jdk/ProblemList-enable-preview.txt @@ -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,6 +25,8 @@ # # List of quarantined tests for testing with --enable-preview # +# These are failures that ONLY occur with the '--enable-preview' option +# specified. There are separate sub-sections for each preview project. +# ############################################################################# - diff --git a/test/jdk/ProblemList-jvmti-stress-agent.txt b/test/jdk/ProblemList-jvmti-stress-agent.txt index 09be19c3d94..e9019b6014e 100644 --- a/test/jdk/ProblemList-jvmti-stress-agent.txt +++ b/test/jdk/ProblemList-jvmti-stress-agent.txt @@ -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 @@ -29,3 +29,16 @@ com/sun/jdi/ThreadMemoryLeakTest.java 0000 # weak referenced are not cleared java/lang/WeakPairMap/Driver.java 0000000 generic-all java/lang/ref/ReachabilityFenceTest.java 0000000 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-shenandoah.txt b/test/jdk/ProblemList-shenandoah.txt index 063795d69e8..522b77cb502 100644 --- a/test/jdk/ProblemList-shenandoah.txt +++ b/test/jdk/ProblemList-shenandoah.txt @@ -58,3 +58,15 @@ jdk/jfr/jcmd/TestJcmdStartPathToGCRoots.java 8342951 generic-all jdk/jfr/jvm/TestWaste.java 8342951 generic-all jdk/jfr/startupargs/TestOldObjectQueueSize.java 8342951 generic-all +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList-zgc.txt b/test/jdk/ProblemList-zgc.txt index ab44c5e47a9..065a52f9a83 100644 --- a/test/jdk/ProblemList-zgc.txt +++ b/test/jdk/ProblemList-zgc.txt @@ -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 @@ -38,3 +38,16 @@ sun/tools/jhsdb/heapconfig/JMapHeapConfigTest.java 8307393 generic-all sun/tools/jhsdb/HeapDumpTestWithActiveProcess.java 8307393 generic-all com/sun/jdi/ThreadMemoryLeakTest.java 8307402 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 5e8406b13a6..36356c3c544 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -778,3 +778,16 @@ java/awt/Cursor/CursorDragTest/ListDragCursor.java 7177297 macosx-all # jdk_since_checks tools/sincechecker/modules/jdk.management.jfr/JdkManagementJfrCheckSince.java 8354921 generic-all + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/langtools/ProblemList-StaticJdk.txt b/test/langtools/ProblemList-StaticJdk.txt index 70f0438c0c6..700099656e8 100644 --- a/test/langtools/ProblemList-StaticJdk.txt +++ b/test/langtools/ProblemList-StaticJdk.txt @@ -1,6 +1,6 @@ ########################################################################### # -# 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,3 +24,16 @@ ########################################################################### # Currently empty + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/langtools/ProblemList-enable-preview.txt b/test/langtools/ProblemList-enable-preview.txt new file mode 100644 index 00000000000..1a831a0dde3 --- /dev/null +++ b/test/langtools/ProblemList-enable-preview.txt @@ -0,0 +1,32 @@ +# +# 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. +# + +############################################################################# +# +# List of quarantined tests for testing with --enable-preview +# +# These are failures that ONLY occur with the '--enable-preview' option +# specified. There are separate sub-sections for each preview project. +# +############################################################################# + diff --git a/test/langtools/ProblemList.txt b/test/langtools/ProblemList.txt index 9d61a20663d..07ab6937c07 100644 --- a/test/langtools/ProblemList.txt +++ b/test/langtools/ProblemList.txt @@ -1,6 +1,6 @@ ########################################################################### # -# 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 @@ -71,3 +71,16 @@ tools/javap/output/RepeatingTypeAnnotations.java ########################################################################### # # jdeps + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/lib-test/ProblemList-StaticJdk.txt b/test/lib-test/ProblemList-StaticJdk.txt index 70f0438c0c6..700099656e8 100644 --- a/test/lib-test/ProblemList-StaticJdk.txt +++ b/test/lib-test/ProblemList-StaticJdk.txt @@ -1,6 +1,6 @@ ########################################################################### # -# 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,3 +24,16 @@ ########################################################################### # Currently empty + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + diff --git a/test/lib-test/ProblemList.txt b/test/lib-test/ProblemList.txt index 011466088d3..2a5caedc8d8 100644 --- a/test/lib-test/ProblemList.txt +++ b/test/lib-test/ProblemList.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 @@ -37,3 +37,16 @@ # More than one label is allowed but must be on the same line. # ############################################################################# + +############################################################################# + +# Preview project specific failures go here at the end of the file. +# +# These are NOT failures that occur with the '--enable-preview' option +# specified; those go in the appropriate ProblemList-enable-preview.txt file. +# These are failures that occur WITHOUT the '--enable-preview' option +# specified AND occur because of some issue with preview project code, +# in either implementation or test code. + +############################################################################# + From ca95e5f3ddd5961dd43f825ed6c47054284c6798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Sat, 31 Jan 2026 23:30:18 +0000 Subject: [PATCH 070/215] 8375580: Avoid using ArrayDeque in jdk.internal.loader.URLClassPath Reviewed-by: liach, redestad, jpai --- .../jdk/internal/loader/URLClassPath.java | 93 ++++----- .../JarManifestClassPathOrder.java | 178 ++++++++++++++++++ 2 files changed, 228 insertions(+), 43 deletions(-) create mode 100644 test/jdk/jdk/internal/loader/URLClassPath/JarManifestClassPathOrder.java diff --git a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java index 3504ce7e8b3..90771575657 100644 --- a/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java +++ b/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, 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 @@ -41,7 +41,6 @@ import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import java.security.CodeSigner; import java.security.cert.Certificate; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -98,11 +97,20 @@ public class URLClassPath { DEBUG_CP_URL_CHECK = p != null ? p.equals("true") || p.isEmpty() : false; } - /* The original search path of URLs. */ - private final ArrayList path; + /* Search path of URLs passed to the constructor or by calls to addURL. + * Access is guarded by a monitor on 'searchPath' itself + */ + private final ArrayList searchPath; - /* The deque of unopened URLs */ - private final ArrayDeque unopenedUrls; + /* Index of the next URL in the search path to process. + * Access is guarded by a monitor on 'searchPath' + */ + private int nextURL = 0; + + /* List of URLs found during expansion of JAR 'Class-Path' attributes. + * Access is guarded by a monitor on 'searchPath' + */ + private final ArrayList manifestClassPath = new ArrayList<>(); /* The resulting search path of Loaders */ private final ArrayList loaders = new ArrayList<>(); @@ -128,14 +136,8 @@ public class URLClassPath { */ public URLClassPath(URL[] urls, URLStreamHandlerFactory factory) { - ArrayList path = new ArrayList<>(urls.length); - ArrayDeque unopenedUrls = new ArrayDeque<>(urls.length); - for (URL url : urls) { - path.add(url); - unopenedUrls.add(url); - } - this.path = path; - this.unopenedUrls = unopenedUrls; + // Reject null URL array or any null element in the array + this.searchPath = new ArrayList<>(List.of(urls)); if (factory != null) { jarHandler = factory.createURLStreamHandler("jar"); @@ -174,16 +176,7 @@ public class URLClassPath { off = next + 1; } while (next != -1); } - - // can't use ArrayDeque#addAll or new ArrayDeque(Collection); - // it's too early in the bootstrap to trigger use of lambdas - int size = path.size(); - ArrayDeque unopenedUrls = new ArrayDeque<>(size); - for (int i = 0; i < size; i++) - unopenedUrls.add(path.get(i)); - - this.unopenedUrls = unopenedUrls; - this.path = path; + this.searchPath = path; // the application class loader uses the built-in protocol handler to avoid protocol // handler lookup when opening JAR files on the class path. this.jarHandler = new sun.net.www.protocol.jar.Handler(); @@ -215,10 +208,9 @@ public class URLClassPath { public synchronized void addURL(URL url) { if (closed || url == null) return; - synchronized (unopenedUrls) { - if (! path.contains(url)) { - unopenedUrls.addLast(url); - path.add(url); + synchronized (searchPath) { + if (! searchPath.contains(url)) { + searchPath.add(url); } } } @@ -249,8 +241,8 @@ public class URLClassPath { * Returns the original search path of URLs. */ public URL[] getURLs() { - synchronized (unopenedUrls) { - return path.toArray(new URL[0]); + synchronized (searchPath) { + return searchPath.toArray(new URL[0]); } } @@ -379,6 +371,23 @@ public class URLClassPath { }; } + /* + * Returns the next URL to process or null if finished + */ + private URL nextURL() { + synchronized (searchPath) { + // Check paths discovered during 'Class-Path' expansion first + if (!manifestClassPath.isEmpty()) { + return manifestClassPath.removeLast(); + } + // Check the regular search path + if (nextURL < searchPath.size()) { + return searchPath.get(nextURL++); + } + // All paths exhausted + return null; + } + } /* * Returns the Loader at the specified position in the URL search * path. The URLs are opened and expanded as needed. Returns null @@ -389,14 +398,13 @@ public class URLClassPath { return null; } // Expand URL search path until the request can be satisfied - // or unopenedUrls is exhausted. + // or all paths are exhausted. while (loaders.size() < index + 1) { - final URL url; - synchronized (unopenedUrls) { - url = unopenedUrls.pollFirst(); - if (url == null) - return null; + final URL url = nextURL(); + if (url == null) { + return null; } + // Skip this URL if it already has a Loader. String urlNoFragString = URLUtil.urlNoFragString(url); if (lmap.containsKey(urlNoFragString)) { @@ -422,7 +430,7 @@ public class URLClassPath { continue; } if (loaderClassPathURLs != null) { - push(loaderClassPathURLs); + addManifestClassPaths(loaderClassPathURLs); } // Finally, add the Loader to the search path. loaders.add(loader); @@ -475,13 +483,12 @@ public class URLClassPath { } /* - * Pushes the specified URLs onto the head of unopened URLs. + * Adds the specified URLs to the list of 'Class-Path' expanded URLs */ - private void push(URL[] urls) { - synchronized (unopenedUrls) { - for (int i = urls.length - 1; i >= 0; --i) { - unopenedUrls.addFirst(urls[i]); - } + private void addManifestClassPaths(URL[] urls) { + synchronized (searchPath) { + // Adding in reversed order since manifestClassPath is consumed tail-first + manifestClassPath.addAll(Arrays.asList(urls).reversed()); } } diff --git a/test/jdk/jdk/internal/loader/URLClassPath/JarManifestClassPathOrder.java b/test/jdk/jdk/internal/loader/URLClassPath/JarManifestClassPathOrder.java new file mode 100644 index 00000000000..c1cda1d448a --- /dev/null +++ b/test/jdk/jdk/internal/loader/URLClassPath/JarManifestClassPathOrder.java @@ -0,0 +1,178 @@ +/* + * 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. + */ + + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/* + * @test + * @summary Verify that URLClassPath discovers JAR Class-Path URLs in the expected order + * @run junit JarManifestClassPathOrder + */ +public class JarManifestClassPathOrder { + + // Name of the JAR resource use for lookups + private static final String ENTRY_NAME = "JarClassPathOrdering.txt"; + + /** + * Verify that URLClassPath discovers JAR files in the expected order when + * only root JARs are on the 'original' class path and the other JARs are + * found via multiple levels of Class-Path Manifest attributes. + * + * @throws IOException if an unexpected error occurs + */ + @Test + public void shouldLoadJARsInExpectedOrder() throws IOException { + + // Set up a 'Class-Path' JAR tree of depth 3 + JarTree specs = new JarTree(); + specs.addJars(3); + + // List of "root" JAR file URLs to use for URLClassLoader + List searchPath = new ArrayList<>(); + + // Order of JAR files we expect URLClassPath to find after a DFS search + List expectedJarNames = new ArrayList<>(); + + for (JarSpec spec : specs.dfs) { + Path jar = createJar(spec); + expectedJarNames.add(jar.getFileName().toString()); + // Only root JARs in the search path, others are discovered transitively via "Class-Path" + if (spec.isRoot()) { + searchPath.add(jar.toUri().toURL()); + } + } + + // Load ENTRY_NAME to identify all JARs found by URLClassPath + try (URLClassLoader loader = new URLClassLoader(searchPath.toArray(new URL[0]))) { + Enumeration resources = loader.getResources(ENTRY_NAME); + // Collect all JAR file names in the order discovered by URLClassPath + List actualJarNames = Collections.list(resources) + .stream() + .map(this::extractJarName) + .collect(Collectors.toList()); + // JARs should be found in DFS order + assertEquals(expectedJarNames, actualJarNames, "JAR files not found in expected order"); + } + } + + // Extract file name of JAR file from a JAR URL + private String extractJarName(URL url) { + String jarPath = url.getPath().substring(0, url.getPath().indexOf("!/")); + String jarName = jarPath.substring(jarPath.lastIndexOf('/') + 1); + return jarName; + } + + // Create a JAR file according to the spec, possibly including a Class-Path attribute + private Path createJar(JarSpec spec) throws IOException { + Path file = Path.of(spec.name +".jar"); + Manifest man = new Manifest(); + Attributes attrs = man.getMainAttributes(); + attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + + // Set Class-Path attribute + if (!spec.children.isEmpty()) { + String path = String.join(" ", spec.children + .stream() + .map(js -> js.name +".jar") + .collect(Collectors.toList())); + attrs.put(Attributes.Name.CLASS_PATH, path); + } + + try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(file), man)) { + // All JARs include the same entry + out.putNextEntry(new JarEntry(ENTRY_NAME)); + } + + return file; + } + + + // Helper class to represent a tree of JARs related by "Class-Path" + static class JarTree { + static final List NAMES = List.of("a", "b", "c"); + List dfs = new ArrayList<>(); + + private void addJar(String prefix, String name, JarSpec parent, int depth, int maxDepth) { + if (depth > maxDepth) { + return; + } + JarSpec spec = new JarSpec(prefix + name, parent); + dfs.add(spec); + + if (parent != null) { + parent.children.add(spec); + } + + for (String childName : NAMES) { + addJar(prefix + name, childName, spec, depth + 1, maxDepth); + } + } + + /* Make a tree of JARs related by the 'Class-Path' Manifest attribute: + * a.jar + * aa.jar + * aaa.jar + * aab.jar + * [...] + * ccc.jar + */ + public void addJars(int maxDepth) { + for (String name : NAMES) { + addJar("", name, null, 1, maxDepth); + } + } + } + + // Helper class to represent a JAR file to be found by URLClassPath + static class JarSpec { + final String name; + final JarSpec parent; + final List children = new ArrayList<>(); + + JarSpec(String name, JarSpec parent) { + this.name = name; + this.parent = parent; + } + boolean isRoot() { + return parent == null; + } + } +} From f4765abd7ef76108c1ae5777f2822800be22030e Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 1 Feb 2026 19:19:18 +0000 Subject: [PATCH 071/215] 8376755: Remove AppContext from Swing javax/swing/plaf/basic classes Reviewed-by: dnguyen, kizune --- .../javax/swing/plaf/basic/BasicButtonUI.java | 14 +---- .../swing/plaf/basic/BasicCheckBoxUI.java | 15 +---- .../swing/plaf/basic/BasicComboBoxUI.java | 19 +------ .../javax/swing/plaf/basic/BasicLabelUI.java | 3 +- .../swing/plaf/basic/BasicLookAndFeel.java | 56 ++++--------------- .../swing/plaf/basic/BasicPopupMenuUI.java | 37 +++++------- .../swing/plaf/basic/BasicRadioButtonUI.java | 14 +---- .../javax/swing/plaf/basic/BasicTextUI.java | 18 ++---- .../swing/plaf/basic/BasicToggleButtonUI.java | 15 +---- .../swing/JPopupMenu/6495920/bug6495920.java | 11 ++-- 10 files changed, 52 insertions(+), 150 deletions(-) diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonUI.java index c7c036e859f..7846e04b565 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, 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 @@ -59,7 +59,6 @@ import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.text.View; -import sun.awt.AppContext; import sun.swing.SwingUtilities2; /** @@ -88,7 +87,7 @@ public class BasicButtonUI extends ButtonUI{ private static final String propertyPrefix = "Button" + "."; - private static final Object BASIC_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new BasicButtonUI(); private KeyListener keyListener = null; @@ -107,14 +106,7 @@ public class BasicButtonUI extends ButtonUI{ * @return an instance of {@code BasicButtonUI} */ public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - BasicButtonUI buttonUI = - (BasicButtonUI) appContext.get(BASIC_BUTTON_UI_KEY); - if (buttonUI == null) { - buttonUI = new BasicButtonUI(); - appContext.put(BASIC_BUTTON_UI_KEY, buttonUI); - } - return buttonUI; + return UI; } /** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicCheckBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicCheckBoxUI.java index 24af28c94c0..49549521005 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicCheckBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, 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 @@ -25,8 +25,6 @@ package javax.swing.plaf.basic; -import sun.awt.AppContext; - import javax.swing.*; import java.awt.*; @@ -51,7 +49,7 @@ import java.io.Serializable; */ public class BasicCheckBoxUI extends BasicRadioButtonUI { - private static final Object BASIC_CHECK_BOX_UI_KEY = new Object(); + private static final ComponentUI UI = new BasicCheckBoxUI(); private static final String propertyPrefix = "CheckBox" + "."; @@ -71,14 +69,7 @@ public class BasicCheckBoxUI extends BasicRadioButtonUI { * @return an instance of {@code BasicCheckBoxUI} */ public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - BasicCheckBoxUI checkboxUI = - (BasicCheckBoxUI) appContext.get(BASIC_CHECK_BOX_UI_KEY); - if (checkboxUI == null) { - checkboxUI = new BasicCheckBoxUI(); - appContext.put(BASIC_CHECK_BOX_UI_KEY, checkboxUI); - } - return checkboxUI; + return UI; } public String getPropertyPrefix() { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java index 2d1a458e0d7..de3cb1adf75 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, 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 @@ -34,7 +34,6 @@ import javax.swing.text.*; import javax.swing.event.*; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; -import sun.awt.AppContext; import sun.swing.DefaultLookup; import sun.swing.SwingUtilities2; import sun.swing.UIAction; @@ -202,10 +201,6 @@ public class BasicComboBoxUI extends ComboBoxUI { // Cached the size that the display needs to render the largest item private Dimension cachedDisplaySize = new Dimension( 0, 0 ); - // Key used for lookup of the DefaultListCellRenderer in the AppContext. - private static final Object COMBO_UI_LIST_CELL_RENDERER_KEY = - new StringBuffer("DefaultListCellRendererKey"); - static final StringBuffer HIDE_POPUP_KEY = new StringBuffer("HidePopupKey"); @@ -237,18 +232,10 @@ public class BasicComboBoxUI extends ComboBoxUI { */ public BasicComboBoxUI() {} + private static final ListCellRenderer CELL_RENDERER = new DefaultListCellRenderer(); // Used for calculating the default size. private static ListCellRenderer getDefaultListCellRenderer() { - @SuppressWarnings("unchecked") - ListCellRenderer renderer = (ListCellRenderer)AppContext. - getAppContext().get(COMBO_UI_LIST_CELL_RENDERER_KEY); - - if (renderer == null) { - renderer = new DefaultListCellRenderer(); - AppContext.getAppContext().put(COMBO_UI_LIST_CELL_RENDERER_KEY, - new DefaultListCellRenderer()); - } - return renderer; + return CELL_RENDERER; } /** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java index 279de053fd6..04251ca6266 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLabelUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, 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 @@ -28,7 +28,6 @@ package javax.swing.plaf.basic; import sun.swing.SwingUtilities2; import sun.swing.DefaultLookup; import sun.swing.UIAction; -import sun.awt.AppContext; import javax.swing.*; import javax.swing.plaf.*; diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java index 12b871d6efd..964ee1cb9c9 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, 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 @@ -78,7 +78,6 @@ import javax.swing.plaf.FontUIResource; import javax.swing.plaf.InsetsUIResource; import javax.swing.text.DefaultEditorKit; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.swing.SwingAccessor; import sun.swing.SwingUtilities2; @@ -126,11 +125,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab AWTEventHelper invocator = null; - /* - * Listen for our AppContext being disposed - */ - private PropertyChangeListener disposer = null; - /** * Constructor for subclasses to call. */ @@ -174,18 +168,6 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab if (invocator == null) { invocator = new AWTEventHelper(); needsEventHelper = true; - - // Add a PropertyChangeListener to our AppContext so we're alerted - // when the AppContext is disposed(), at which time this laf should - // be uninitialize()d. - disposer = new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent prpChg) { - uninitialize(); - } - }; - AppContext.getAppContext().addPropertyChangeListener( - AppContext.GUI_DISPOSED, - disposer); } } @@ -193,35 +175,21 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab * {@inheritDoc} */ public void uninitialize() { - AppContext context = AppContext.getAppContext(); - synchronized (BasicPopupMenuUI.MOUSE_GRABBER_KEY) { - Object grabber = context.get(BasicPopupMenuUI.MOUSE_GRABBER_KEY); - if (grabber != null) { - ((BasicPopupMenuUI.MouseGrabber)grabber).uninstall(); - } - } - synchronized (BasicPopupMenuUI.MENU_KEYBOARD_HELPER_KEY) { - Object helper = - context.get(BasicPopupMenuUI.MENU_KEYBOARD_HELPER_KEY); - if (helper != null) { - ((BasicPopupMenuUI.MenuKeyboardHelper)helper).uninstall(); - } - } + synchronized (BasicPopupMenuUI.class) { + if (BasicPopupMenuUI.mouseGrabber != null) { + BasicPopupMenuUI.mouseGrabber.uninstall(); + BasicPopupMenuUI.mouseGrabber = null; + } + if (BasicPopupMenuUI.menuKeyboardHelper != null) { + BasicPopupMenuUI.menuKeyboardHelper.uninstall(); + BasicPopupMenuUI.menuKeyboardHelper = null; + } + } - if(invocator != null) { + if (invocator != null) { invocator.run(); invocator = null; } - - if (disposer != null) { - // Note that we're likely calling removePropertyChangeListener() - // during the course of AppContext.firePropertyChange(). - // However, EventListenerAggregate has code to safely modify - // the list under such circumstances. - context.removePropertyChangeListener(AppContext.GUI_DISPOSED, - disposer); - disposer = null; - } } /** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java index 6fab795e36c..8df27d3e3cb 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -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 @@ -41,8 +41,6 @@ import java.util.*; import sun.swing.UIAction; -import sun.awt.AppContext; - /** * A Windows L&F implementation of PopupMenuUI. This implementation * is a "combined" view/controller. @@ -52,10 +50,9 @@ import sun.awt.AppContext; * @author Arnaud Weber */ public class BasicPopupMenuUI extends PopupMenuUI { - static final StringBuilder MOUSE_GRABBER_KEY = new StringBuilder( - "javax.swing.plaf.basic.BasicPopupMenuUI.MouseGrabber"); - static final StringBuilder MENU_KEYBOARD_HELPER_KEY = new StringBuilder( - "javax.swing.plaf.basic.BasicPopupMenuUI.MenuKeyboardHelper"); + + static MouseGrabber mouseGrabber = new MouseGrabber(); + static MenuKeyboardHelper menuKeyboardHelper = null; /** * The instance of {@code JPopupMenu}. @@ -126,23 +123,18 @@ public class BasicPopupMenuUI extends PopupMenuUI { } popupMenu.addMenuKeyListener(menuKeyListener); - AppContext context = AppContext.getAppContext(); - synchronized (MOUSE_GRABBER_KEY) { - MouseGrabber mouseGrabber = (MouseGrabber)context.get( - MOUSE_GRABBER_KEY); - if (mouseGrabber == null) { - mouseGrabber = new MouseGrabber(); - context.put(MOUSE_GRABBER_KEY, mouseGrabber); - } + synchronized (MouseGrabber.class) { + if (mouseGrabber == null) mouseGrabber = new MouseGrabber(); } - synchronized (MENU_KEYBOARD_HELPER_KEY) { - MenuKeyboardHelper helper = - (MenuKeyboardHelper)context.get(MENU_KEYBOARD_HELPER_KEY); + + synchronized (BasicPopupMenuUI.class) { + MenuKeyboardHelper helper = menuKeyboardHelper; if (helper == null) { helper = new MenuKeyboardHelper(); - context.put(MENU_KEYBOARD_HELPER_KEY, helper); MenuSelectionManager msm = MenuSelectionManager.defaultManager(); msm.addChangeListener(helper); + menuKeyboardHelper = helper; + } } } @@ -768,10 +760,10 @@ public class BasicPopupMenuUI extends PopupMenuUI { } void uninstall() { - synchronized (MOUSE_GRABBER_KEY) { + synchronized (MouseGrabber.class) { MenuSelectionManager.defaultManager().removeChangeListener(this); ungrabWindow(); - AppContext.getAppContext().remove(MOUSE_GRABBER_KEY); + mouseGrabber = null; } } @@ -1233,9 +1225,8 @@ public class BasicPopupMenuUI extends PopupMenuUI { } void uninstall() { - synchronized (MENU_KEYBOARD_HELPER_KEY) { + synchronized (BasicPopupMenuUI.class) { MenuSelectionManager.defaultManager().removeChangeListener(this); - AppContext.getAppContext().remove(MENU_KEYBOARD_HELPER_KEY); } } } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java index 9439059e03e..fdc68d3244f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, 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 @@ -30,7 +30,6 @@ import javax.swing.*; import javax.swing.plaf.*; import javax.swing.text.View; import sun.swing.SwingUtilities2; -import sun.awt.AppContext; /** * RadioButtonUI implementation for BasicRadioButtonUI @@ -39,7 +38,7 @@ import sun.awt.AppContext; */ public class BasicRadioButtonUI extends BasicToggleButtonUI { - private static final Object BASIC_RADIO_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new BasicRadioButtonUI(); /** * The icon. @@ -66,14 +65,7 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI * @return an instance of {@code BasicRadioButtonUI} */ public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - BasicRadioButtonUI radioButtonUI = - (BasicRadioButtonUI) appContext.get(BASIC_RADIO_BUTTON_UI_KEY); - if (radioButtonUI == null) { - radioButtonUI = new BasicRadioButtonUI(); - appContext.put(BASIC_RADIO_BUTTON_UI_KEY, radioButtonUI); - } - return radioButtonUI; + return UI; } @Override diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java index 391cfdf0b65..d144b5b7240 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicTextUI.java @@ -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 @@ -41,7 +41,6 @@ import javax.swing.border.Border; import javax.swing.plaf.UIResource; import javax.swing.plaf.synth.SynthUI; import sun.swing.DefaultLookup; -import sun.awt.AppContext; import sun.swing.SwingUtilities2; import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag; @@ -2234,24 +2233,19 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory { } } + private static volatile DragListener dragListenerSingleton; + private static DragListener getDragListener() { synchronized(DragListener.class) { - DragListener listener = - (DragListener)AppContext.getAppContext(). - get(DragListener.class); - - if (listener == null) { - listener = new DragListener(); - AppContext.getAppContext().put(DragListener.class, listener); + if (dragListenerSingleton == null) { + dragListenerSingleton = new DragListener(); } - - return listener; + return dragListenerSingleton; } } /** * Listens for mouse events for the purposes of detecting drag gestures. - * BasicTextUI will maintain one of these per AppContext. */ static class DragListener extends MouseInputAdapter implements BeforeDrag { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToggleButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToggleButtonUI.java index 2b68fe2b4f3..1a7dfe87353 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToggleButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, 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 @@ -25,8 +25,6 @@ package javax.swing.plaf.basic; -import sun.awt.AppContext; - import java.awt.*; import java.awt.event.*; @@ -44,7 +42,7 @@ import javax.swing.text.View; */ public class BasicToggleButtonUI extends BasicButtonUI { - private static final Object BASIC_TOGGLE_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new BasicToggleButtonUI(); private static final String propertyPrefix = "ToggleButton" + "."; @@ -64,14 +62,7 @@ public class BasicToggleButtonUI extends BasicButtonUI { * @return an instance of {@code BasicToggleButtonUI} */ public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - BasicToggleButtonUI toggleButtonUI = - (BasicToggleButtonUI) appContext.get(BASIC_TOGGLE_BUTTON_UI_KEY); - if (toggleButtonUI == null) { - toggleButtonUI = new BasicToggleButtonUI(); - appContext.put(BASIC_TOGGLE_BUTTON_UI_KEY, toggleButtonUI); - } - return toggleButtonUI; + return UI; } protected String getPropertyPrefix() { diff --git a/test/jdk/javax/swing/JPopupMenu/6495920/bug6495920.java b/test/jdk/javax/swing/JPopupMenu/6495920/bug6495920.java index 6a8c132c4e4..cc37cb97b18 100644 --- a/test/jdk/javax/swing/JPopupMenu/6495920/bug6495920.java +++ b/test/jdk/javax/swing/JPopupMenu/6495920/bug6495920.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, 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 @@ -29,12 +29,9 @@ interaction with GNOME is not crippled * @author Sergey Malenkov * @library ../.. - * @modules java.desktop/sun.awt * @modules java.desktop/javax.swing.plaf.basic:open */ -import sun.awt.AppContext; - import java.awt.Point; import java.awt.Robot; import java.awt.event.InputEvent; @@ -92,12 +89,12 @@ public class bug6495920 implements Thread.UncaughtExceptionHandler { } public void thirdValidate() throws Exception { - Field key = BasicPopupMenuUI.class.getDeclaredField("MOUSE_GRABBER_KEY"); + Field key = BasicPopupMenuUI.class.getDeclaredField("mouseGrabber"); key.setAccessible(true); - Object grabber = AppContext.getAppContext().get(key.get(null)); + Object grabber = key.get(null); if (grabber == null) { - throw new Exception("cannot find a mouse grabber in app's context"); + throw new Exception("cannot find a mouse grabber"); } Field field = grabber.getClass().getDeclaredField("grabbedWindow"); From 3a32757743b459902aa97092d95eb9b0cb3099d6 Mon Sep 17 00:00:00 2001 From: Feilong Jiang Date: Mon, 2 Feb 2026 02:15:42 +0000 Subject: [PATCH 072/215] 8376572: RISC-V: Interpreter: Load array index as signed int Reviewed-by: fyang, dzhang --- src/hotspot/cpu/riscv/templateTable_riscv.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 5a3644f70bb..0fb529d1683 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -708,7 +708,6 @@ void TemplateTable::index_check(Register array, Register index) { __ mv(x11, index); } Label ok; - __ sext(index, index, 32); __ bltu(index, length, ok); __ mv(x13, array); __ mv(t1, Interpreter::_throw_ArrayIndexOutOfBoundsException_entry); @@ -1052,7 +1051,7 @@ void TemplateTable::aastore() { transition(vtos, vtos); // stack: ..., array, index, value __ ld(x10, at_tos()); // value - __ ld(x12, at_tos_p1()); // index + __ lw(x12, at_tos_p1()); // index __ ld(x13, at_tos_p2()); // array index_check(x13, x12); // kills x11 @@ -1462,9 +1461,9 @@ void TemplateTable::iinc() { transition(vtos, vtos); __ load_signed_byte(x11, at_bcp(2)); // get constant locals_index(x12); - __ ld(x10, iaddress(x12, x10, _masm)); + __ lw(x10, iaddress(x12, x10, _masm)); __ addw(x10, x10, x11); - __ sd(x10, iaddress(x12, t0, _masm)); + __ sw(x10, iaddress(x12, t0, _masm)); } void TemplateTable::wide_iinc() { @@ -1477,9 +1476,9 @@ void TemplateTable::wide_iinc() { __ orr(x11, x11, t1); locals_index_wide(x12); - __ ld(x10, iaddress(x12, t0, _masm)); + __ lw(x10, iaddress(x12, t0, _masm)); __ addw(x10, x10, x11); - __ sd(x10, iaddress(x12, t0, _masm)); + __ sw(x10, iaddress(x12, t0, _masm)); } void TemplateTable::convert() { From f8b0ff26c9e6643e96f06c18c509ddaf50326205 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 2 Feb 2026 07:12:32 +0000 Subject: [PATCH 073/215] 8376472: Shenandoah: Assembler store barriers read destination memory despite the decorators Reviewed-by: mdoerr, wkemper --- .../shenandoahBarrierSetAssembler_aarch64.cpp | 80 +++++++------- .../shenandoahBarrierSetAssembler_aarch64.hpp | 25 ++--- .../shenandoahBarrierSetAssembler_ppc.cpp | 55 ++++++---- .../shenandoahBarrierSetAssembler_ppc.hpp | 24 ++--- .../shenandoahBarrierSetAssembler_riscv.cpp | 82 +++++++------- .../shenandoahBarrierSetAssembler_riscv.hpp | 25 ++--- .../shenandoahBarrierSetAssembler_x86.cpp | 101 ++++++++---------- .../shenandoahBarrierSetAssembler_x86.hpp | 21 ++-- .../gc/shenandoah/shenandoahBarrierSet.cpp | 18 +++- .../gc/shenandoah/shenandoahBarrierSet.hpp | 2 + 10 files changed, 210 insertions(+), 223 deletions(-) diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 9a035d9f40e..ad7bac4e067 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -85,26 +85,16 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call) { - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, thread, tmp, rscratch1, tosca_live, expand_call); - } -} +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -358,20 +348,20 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { __ enter(/*strip_ret_addr*/true); __ push_call_clobbered_registers(); - satb_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - rthread /* thread */, - tmp1 /* tmp1 */, - tmp2 /* tmp2 */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + rthread /* thread */, + tmp1 /* tmp1 */, + tmp2 /* tmp2 */, + true /* tosca_live */, + true /* expand_call */); __ pop_call_clobbered_registers(); __ leave(); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); __ lsr(obj, obj, CardTable::card_shift()); @@ -394,13 +384,13 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - if (!on_oop) { + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); return; } - // flatten object address if needed + // Flatten object address right away for simplicity: likely needed by barriers if (dst.index() == noreg && dst.offset() == 0) { if (dst.base() != tmp3) { __ mov(tmp3, dst.base()); @@ -409,20 +399,26 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ lea(tmp3, dst); } - shenandoah_write_barrier_pre(masm, - tmp3 /* obj */, - tmp2 /* pre_val */, - rthread /* thread */, - tmp1 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); + bool storing_non_null = (val != noreg); + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp3 /* obj */, + tmp2 /* pre_val */, + rthread /* thread */, + tmp1 /* tmp */, + rscratch1 /* tmp2 */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } + + // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); - bool in_heap = (decorators & IN_HEAP) != 0; - bool needs_post_barrier = (val != noreg) && in_heap && ShenandoahCardBarrier; - if (needs_post_barrier) { - store_check(masm, tmp3); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp3); } } diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index c0e708e1292..362fcae1ccd 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -40,23 +40,16 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call); - void store_check(MacroAssembler* masm, Register obj); + void card_barrier(MacroAssembler* masm, Register obj); void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 9d143c14d27..c3bb1811031 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -50,14 +50,14 @@ #define __ masm-> -void ShenandoahBarrierSetAssembler::satb_write_barrier(MacroAssembler *masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level) { +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler *masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp1, Register tmp2, Register tmp3, + MacroAssembler::PreservationLevel preservation_level) { if (ShenandoahSATBBarrier) { - __ block_comment("satb_write_barrier (shenandoahgc) {"); - satb_write_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); - __ block_comment("} satb_write_barrier (shenandoahgc)"); + __ block_comment("satb_barrier (shenandoahgc) {"); + satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + __ block_comment("} satb_barrier (shenandoahgc)"); } } @@ -198,11 +198,12 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec // In "load mode", this register acts as a temporary register and must // thus not be 'noreg'. In "preloaded mode", its content will be sustained. // tmp1/tmp2: Temporary registers, one of which must be non-volatile in "preloaded mode". -void ShenandoahBarrierSetAssembler::satb_write_barrier_impl(MacroAssembler *masm, DecoratorSet decorators, - Register base, RegisterOrConstant ind_or_offs, - Register pre_val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { +void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, DecoratorSet decorators, + Register base, RegisterOrConstant ind_or_offs, + Register pre_val, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); assert_different_registers(tmp1, tmp2, pre_val, noreg); Label skip_barrier; @@ -574,13 +575,13 @@ void ShenandoahBarrierSetAssembler::load_at( if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { if (ShenandoahSATBBarrier) { __ block_comment("keep_alive_barrier (shenandoahgc) {"); - satb_write_barrier_impl(masm, 0, noreg, noreg, dst, tmp1, tmp2, preservation_level); + satb_barrier_impl(masm, 0, noreg, noreg, dst, tmp1, tmp2, preservation_level); __ block_comment("} keep_alive_barrier (shenandoahgc)"); } } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); assert_different_registers(base, tmp, R0); @@ -603,21 +604,33 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler *masm, DecoratorSet Register base, RegisterOrConstant ind_or_offs, Register val, Register tmp1, Register tmp2, Register tmp3, MacroAssembler::PreservationLevel preservation_level) { - if (is_reference_type(type)) { - if (ShenandoahSATBBarrier) { - satb_write_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); - } + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { + BarrierSetAssembler::store_at(masm, decorators, type, + base, ind_or_offs, + val, + tmp1, tmp2, tmp3, + preservation_level); + return; } + bool storing_non_null = (val != noreg); + + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + } + + // Store! BarrierSetAssembler::store_at(masm, decorators, type, base, ind_or_offs, val, tmp1, tmp2, tmp3, preservation_level); - // No need for post barrier if storing null - if (ShenandoahCardBarrier && is_reference_type(type) && val != noreg) { - store_check(masm, base, ind_or_offs, tmp1); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, base, ind_or_offs, tmp1); } } diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index b058dcf1a2e..52615a740af 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -45,15 +45,15 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: /* ==== Actual barrier implementations ==== */ - void satb_write_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, - Register base, RegisterOrConstant ind_or_offs, - Register pre_val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + void satb_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, + Register base, RegisterOrConstant ind_or_offs, + Register pre_val, + Register tmp1, Register tmp2, + MacroAssembler::PreservationLevel preservation_level); - void store_check(MacroAssembler* masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp); + void card_barrier(MacroAssembler* masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp); void load_reference_barrier_impl(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, @@ -85,10 +85,10 @@ public: #endif /* ==== Available barriers (facades of the actual implementations) ==== */ - void satb_write_barrier(MacroAssembler* masm, - Register base, RegisterOrConstant ind_or_offs, - Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level); + void satb_barrier(MacroAssembler* masm, + Register base, RegisterOrConstant ind_or_offs, + Register tmp1, Register tmp2, Register tmp3, + MacroAssembler::PreservationLevel preservation_level); void load_reference_barrier(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index dd6c8556307..3cbbb783258 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -88,26 +88,16 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call) { - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, thread, tmp, t0, tosca_live, expand_call); - } -} +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -376,21 +366,21 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { __ enter(); __ push_call_clobbered_registers(); - satb_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - xthread /* thread */, - tmp1 /* tmp1 */, - tmp2 /* tmp2 */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + xthread /* thread */, + tmp1 /* tmp1 */, + tmp2 /* tmp2 */, + true /* tosca_live */, + true /* expand_call */); __ pop_call_clobbered_registers(); __ leave(); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { - assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?"); +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { + assert(ShenandoahCardBarrier, "Should have been checked by caller"); __ srli(obj, obj, CardTable::card_shift()); @@ -413,13 +403,13 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - if (!on_oop) { + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); return; } - // flatten object address if needed + // Flatten object address right away for simplicity: likely needed by barriers if (dst.offset() == 0) { if (dst.base() != tmp3) { __ mv(tmp3, dst.base()); @@ -428,20 +418,26 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet __ la(tmp3, dst); } - shenandoah_write_barrier_pre(masm, - tmp3 /* obj */, - tmp2 /* pre_val */, - xthread /* thread */, - tmp1 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); + bool storing_non_null = (val != noreg); + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp3 /* obj */, + tmp2 /* pre_val */, + xthread /* thread */, + tmp1 /* tmp */, + t0 /* tmp2 */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } + + // Store! BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); - bool in_heap = (decorators & IN_HEAP) != 0; - bool needs_post_barrier = (val != noreg) && in_heap && ShenandoahCardBarrier; - if (needs_post_barrier) { - store_check(masm, tmp3); + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp3); } } diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index c8a7c35fb83..5085be26b2e 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -41,23 +41,16 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp1, - Register tmp2, - bool tosca_live, - bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register thread, - Register tmp, - bool tosca_live, - bool expand_call); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register thread, + Register tmp1, + Register tmp2, + bool tosca_live, + bool expand_call); - void store_check(MacroAssembler* masm, Register obj); + void card_barrier(MacroAssembler* masm, Register obj); void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 9e321391f6c..97829a10a3b 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -174,24 +174,14 @@ void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, Dec } } -void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call) { +void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp, + bool tosca_live, + bool expand_call) { + assert(ShenandoahSATBBarrier, "Should be checked by caller"); - if (ShenandoahSATBBarrier) { - satb_write_barrier_pre(masm, obj, pre_val, tmp, tosca_live, expand_call); - } -} - -void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call) { // If expand_call is true then we expand the call_VM_leaf macro // directly to skip generating the check by // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp. @@ -533,18 +523,18 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d assert_different_registers(dst, tmp1, r15_thread); // Generate the SATB pre-barrier code to log the value of // the referent field in an SATB buffer. - shenandoah_write_barrier_pre(masm /* masm */, - noreg /* obj */, - dst /* pre_val */, - tmp1 /* tmp */, - true /* tosca_live */, - true /* expand_call */); + satb_barrier(masm /* masm */, + noreg /* obj */, + dst /* pre_val */, + tmp1 /* tmp */, + true /* tosca_live */, + true /* expand_call */); restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); } } -void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) { +void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); // Does a store check for the oop in register obj. The content of @@ -575,41 +565,40 @@ void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register o void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { - bool on_oop = is_reference_type(type); - bool in_heap = (decorators & IN_HEAP) != 0; - bool as_normal = (decorators & AS_NORMAL) != 0; - if (on_oop && in_heap) { - bool needs_pre_barrier = as_normal; + // 1: non-reference types require no barriers + if (!is_reference_type(type)) { + BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); + return; + } - // flatten object address if needed - // We do it regardless of precise because we need the registers - if (dst.index() == noreg && dst.disp() == 0) { - if (dst.base() != tmp1) { - __ movptr(tmp1, dst.base()); - } - } else { - __ lea(tmp1, dst); - } - - assert_different_registers(val, tmp1, tmp2, tmp3, r15_thread); - - if (needs_pre_barrier) { - shenandoah_write_barrier_pre(masm /*masm*/, - tmp1 /* obj */, - tmp2 /* pre_val */, - tmp3 /* tmp */, - val != noreg /* tosca_live */, - false /* expand_call */); - } - - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); - if (val != noreg) { - if (ShenandoahCardBarrier) { - store_check(masm, tmp1); - } + // Flatten object address right away for simplicity: likely needed by barriers + assert_different_registers(val, tmp1, tmp2, tmp3, r15_thread); + if (dst.index() == noreg && dst.disp() == 0) { + if (dst.base() != tmp1) { + __ movptr(tmp1, dst.base()); } } else { - BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); + __ lea(tmp1, dst); + } + + bool storing_non_null = (val != noreg); + + // 2: pre-barrier: SATB needs the previous value + if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) { + satb_barrier(masm, + tmp1 /* obj */, + tmp2 /* pre_val */, + tmp3 /* tmp */, + storing_non_null /* tosca_live */, + false /* expand_call */); + } + + // Store! + BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); + + // 3: post-barrier: card barrier needs store address + if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) { + card_barrier(masm, tmp1); } } diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index b0185f2dbff..b5cc5c8d834 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -41,21 +41,14 @@ class StubCodeGenerator; class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { private: - void satb_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call); + void satb_barrier(MacroAssembler* masm, + Register obj, + Register pre_val, + Register tmp, + bool tosca_live, + bool expand_call); - void shenandoah_write_barrier_pre(MacroAssembler* masm, - Register obj, - Register pre_val, - Register tmp, - bool tosca_live, - bool expand_call); - - void store_check(MacroAssembler* masm, Register obj); + void card_barrier(MacroAssembler* masm, Register obj); void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, Register addr, Register count, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index 2aa37d7c575..004558a9fa8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -72,21 +72,33 @@ void ShenandoahBarrierSet::print_on(outputStream* st) const { bool ShenandoahBarrierSet::need_load_reference_barrier(DecoratorSet decorators, BasicType type) { if (!ShenandoahLoadRefBarrier) return false; - // Only needed for references return is_reference_type(type); } bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators, BasicType type) { if (!ShenandoahSATBBarrier) return false; - // Only needed for references if (!is_reference_type(type)) return false; - bool keep_alive = (decorators & AS_NO_KEEPALIVE) == 0; bool unknown = (decorators & ON_UNKNOWN_OOP_REF) != 0; bool on_weak_ref = (decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF)) != 0; return (on_weak_ref || unknown) && keep_alive; } +bool ShenandoahBarrierSet::need_satb_barrier(DecoratorSet decorators, BasicType type) { + if (!ShenandoahSATBBarrier) return false; + if (!is_reference_type(type)) return false; + bool as_normal = (decorators & AS_NORMAL) != 0; + bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; + return as_normal && !dest_uninitialized; +} + +bool ShenandoahBarrierSet::need_card_barrier(DecoratorSet decorators, BasicType type) { + if (!ShenandoahCardBarrier) return false; + if (!is_reference_type(type)) return false; + bool in_heap = (decorators & IN_HEAP) != 0; + return in_heap; +} + void ShenandoahBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop new_obj) { #if COMPILER2_OR_JVMCI if (ReduceInitialCardMarks && ShenandoahCardBarrier && !ShenandoahHeap::heap()->is_in_young(new_obj)) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index 28765605267..e7a0ed57740 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -60,6 +60,8 @@ public: static bool need_load_reference_barrier(DecoratorSet decorators, BasicType type); static bool need_keep_alive_barrier(DecoratorSet decorators, BasicType type); + static bool need_satb_barrier(DecoratorSet decorators, BasicType type); + static bool need_card_barrier(DecoratorSet decorators, BasicType type); static bool is_strong_access(DecoratorSet decorators) { return (decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF)) == 0; From f22bc1cd518bc7f09dc49b78e40d06210226d2b7 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 2 Feb 2026 07:58:01 +0000 Subject: [PATCH 074/215] 8376131: Convert ContiguousSpace to use Atomic Reviewed-by: dholmes, kbarrett --- src/hotspot/share/gc/shared/space.cpp | 12 ++++-------- src/hotspot/share/gc/shared/space.hpp | 14 ++++++-------- src/hotspot/share/gc/shared/vmStructs_gc.hpp | 4 ++-- src/hotspot/share/runtime/vmStructs.cpp | 3 ++- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/hotspot/share/gc/shared/space.cpp b/src/hotspot/share/gc/shared/space.cpp index 011a0f5cfd8..84ba21527fd 100644 --- a/src/hotspot/share/gc/shared/space.cpp +++ b/src/hotspot/share/gc/shared/space.cpp @@ -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 @@ -30,7 +30,6 @@ #include "memory/iterator.inline.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/java.hpp" #include "runtime/safepoint.hpp" #include "utilities/align.hpp" @@ -69,7 +68,7 @@ void ContiguousSpace::clear(bool mangle_space) { #ifndef PRODUCT void ContiguousSpace::mangle_unused_area() { - mangle_unused_area(MemRegion(_top, _end)); + mangle_unused_area(MemRegion(top(), _end)); } void ContiguousSpace::mangle_unused_area(MemRegion mr) { @@ -128,11 +127,8 @@ inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size) { HeapWord* obj = top(); if (pointer_delta(end(), obj) >= size) { HeapWord* new_top = obj + size; - HeapWord* result = AtomicAccess::cmpxchg(top_addr(), obj, new_top); - // result can be one of two: - // the old top value: the exchange succeeded - // otherwise: the new value of the top is returned. - if (result == obj) { + // Retry if we did not successfully updated the top pointers. + if (_top.compare_set(obj, new_top)) { assert(is_object_aligned(obj) && is_object_aligned(new_top), "checking alignment"); return obj; } diff --git a/src/hotspot/share/gc/shared/space.hpp b/src/hotspot/share/gc/shared/space.hpp index 7f2887275b3..05b22f680bf 100644 --- a/src/hotspot/share/gc/shared/space.hpp +++ b/src/hotspot/share/gc/shared/space.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 @@ -32,6 +32,7 @@ #include "memory/iterator.hpp" #include "memory/memRegion.hpp" #include "oops/markWord.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/align.hpp" #include "utilities/macros.hpp" @@ -53,7 +54,7 @@ class ContiguousSpace: public CHeapObj { private: HeapWord* _bottom; HeapWord* _end; - HeapWord* _top; + Atomic _top; // Allocation helpers (return null if full). inline HeapWord* allocate_impl(size_t word_size); @@ -64,12 +65,12 @@ public: // Accessors HeapWord* bottom() const { return _bottom; } - HeapWord* end() const { return _end; } - HeapWord* top() const { return _top; } + HeapWord* end() const { return _end; } + HeapWord* top() const { return _top.load_relaxed(); } void set_bottom(HeapWord* value) { _bottom = value; } void set_end(HeapWord* value) { _end = value; } - void set_top(HeapWord* value) { _top = value; } + void set_top(HeapWord* value) { _top.store_relaxed(value); } // Testers bool is_empty() const { return used() == 0; } @@ -121,9 +122,6 @@ public: // Iteration void object_iterate(ObjectClosure* blk); - // Addresses for inlined allocation - HeapWord** top_addr() { return &_top; } - // Debugging void verify() const; }; diff --git a/src/hotspot/share/gc/shared/vmStructs_gc.hpp b/src/hotspot/share/gc/shared/vmStructs_gc.hpp index db968e28f67..6a29eb25b37 100644 --- a/src/hotspot/share/gc/shared/vmStructs_gc.hpp +++ b/src/hotspot/share/gc/shared/vmStructs_gc.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 @@ -97,7 +97,7 @@ \ nonstatic_field(ContiguousSpace, _bottom, HeapWord*) \ nonstatic_field(ContiguousSpace, _end, HeapWord*) \ - nonstatic_field(ContiguousSpace, _top, HeapWord*) \ + nonstatic_field(ContiguousSpace, _top, Atomic) \ \ nonstatic_field(MemRegion, _start, HeapWord*) \ nonstatic_field(MemRegion, _word_size, size_t) diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 4ecc8f9ca01..02572e16728 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -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 @@ -901,6 +901,7 @@ /*****************************/ \ \ declare_toplevel_type(void*) \ + declare_toplevel_type(Atomic) \ declare_toplevel_type(int*) \ declare_toplevel_type(char*) \ declare_toplevel_type(char**) \ From 766e03b151b2972108ddc207eed10428e9a91c30 Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Mon, 2 Feb 2026 08:02:07 +0000 Subject: [PATCH 075/215] 8367993: G1: Speed up ConcurrentMark initialization Reviewed-by: sjohanss, tschatzl --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 23 ++++++----- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 1 - src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 39 ++++++++++++++----- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 3 ++ src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp | 2 +- src/hotspot/share/gc/g1/g1Policy.cpp | 4 +- .../share/gc/g1/g1RegionMarkStatsCache.cpp | 2 +- src/hotspot/share/gc/g1/g1VMOperations.cpp | 2 +- src/hotspot/share/prims/whitebox.cpp | 2 +- 9 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 8f83a653885..9424a804bd8 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -1320,7 +1320,6 @@ G1CollectedHeap::G1CollectedHeap() : _card_set_freelist_pool(G1CardSetConfiguration::num_mem_object_types()), _young_regions_cset_group(card_set_config(), &_card_set_freelist_pool, G1CSetCandidateGroup::YoungRegionId), _cm(nullptr), - _cm_thread(nullptr), _cr(nullptr), _task_queues(nullptr), _partial_array_state_manager(nullptr), @@ -1564,7 +1563,6 @@ jint G1CollectedHeap::initialize() { // Create the G1ConcurrentMark data structure and thread. // (Must do this late, so that "max_[reserved_]regions" is defined.) _cm = new G1ConcurrentMark(this, bitmap_storage); - _cm_thread = _cm->cm_thread(); // Now expand into the initial heap size. if (!expand(init_byte_size, _workers)) { @@ -1636,7 +1634,9 @@ jint G1CollectedHeap::initialize() { } bool G1CollectedHeap::concurrent_mark_is_terminating() const { - return _cm_thread->should_terminate(); + assert(_cm != nullptr, "_cm must have been created"); + assert(_cm->is_fully_initialized(), "thread must exist in order to check if mark is terminating"); + return _cm->cm_thread()->should_terminate(); } void G1CollectedHeap::stop() { @@ -1645,7 +1645,9 @@ void G1CollectedHeap::stop() { // that are destroyed during shutdown. _cr->stop(); _service_thread->stop(); - _cm_thread->stop(); + if (_cm->is_fully_initialized()) { + _cm->cm_thread()->stop(); + } } void G1CollectedHeap::safepoint_synchronize_begin() { @@ -1842,7 +1844,7 @@ void G1CollectedHeap::increment_old_marking_cycles_completed(bool concurrent, // is set) so that if a waiter requests another System.gc() it doesn't // incorrectly see that a marking cycle is still in progress. if (concurrent) { - _cm_thread->set_idle(); + _cm->cm_thread()->set_idle(); } // Notify threads waiting in System.gc() (with ExplicitGCInvokesConcurrent) @@ -2421,7 +2423,6 @@ void G1CollectedHeap::print_gc_on(outputStream* st) const { void G1CollectedHeap::gc_threads_do(ThreadClosure* tc) const { workers()->threads_do(tc); - tc->do_thread(_cm_thread); _cm->threads_do(tc); _cr->threads_do(tc); tc->do_thread(_service_thread); @@ -2542,15 +2543,15 @@ HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size, } void G1CollectedHeap::start_concurrent_cycle(bool concurrent_operation_is_full_mark) { - assert(!_cm_thread->in_progress(), "Can not start concurrent operation while in progress"); - + assert(_cm->is_fully_initialized(), "sanity"); + assert(!_cm->in_progress(), "Can not start concurrent operation while in progress"); MutexLocker x(G1CGC_lock, Mutex::_no_safepoint_check_flag); if (concurrent_operation_is_full_mark) { _cm->post_concurrent_mark_start(); - _cm_thread->start_full_mark(); + _cm->cm_thread()->start_full_mark(); } else { _cm->post_concurrent_undo_start(); - _cm_thread->start_undo_mark(); + _cm->cm_thread()->start_undo_mark(); } G1CGC_lock->notify(); } @@ -2726,6 +2727,8 @@ void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_si _bytes_used_during_gc = 0; + _cm->fully_initialize(); + policy()->decide_on_concurrent_start_pause(); // Record whether this pause may need to trigger a concurrent operation. Later, // when we signal the G1ConcurrentMarkThread, the collector state has already diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 1f900c76851..8ff9d481000 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -823,7 +823,6 @@ public: // The concurrent marker (and the thread it runs in.) G1ConcurrentMark* _cm; - G1ConcurrentMarkThread* _cm_thread; // The concurrent refiner. G1ConcurrentRefine* _cr; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 2bbfb5032b3..29de5a12599 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -473,7 +473,7 @@ bool G1CMRootMemRegions::wait_until_scan_finished() { G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, G1RegionToSpaceMapper* bitmap_storage) : - // _cm_thread set inside the constructor + _cm_thread(nullptr), _g1h(g1h), _mark_bitmap(), @@ -484,13 +484,12 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _global_mark_stack(), - // _finger set in set_non_marking_state + _finger(nullptr), // _finger set in set_non_marking_state _worker_id_offset(G1ConcRefinementThreads), // The refinement control thread does not refine cards, so it's just the worker threads. _max_num_tasks(MAX2(ConcGCThreads, ParallelGCThreads)), - // _num_active_tasks set in set_non_marking_state() - // _tasks set inside the constructor - + _num_active_tasks(0), // _num_active_tasks set in set_non_marking_state() + _tasks(nullptr), // _tasks set inside late_init() _task_queues(new G1CMTaskQueueSet(_max_num_tasks)), _terminator(_max_num_tasks, _task_queues), _partial_array_state_manager(new PartialArrayStateManager(_max_num_tasks)), @@ -525,6 +524,12 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, assert(G1CGC_lock != nullptr, "CGC_lock must be initialized"); _mark_bitmap.initialize(g1h->reserved(), bitmap_storage); +} + +void G1ConcurrentMark::fully_initialize() { + if (is_fully_initialized()) { + return; + } // Create & start ConcurrentMark thread. _cm_thread = new G1ConcurrentMarkThread(this); @@ -560,6 +565,10 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, reset_at_marking_complete(); } +bool G1ConcurrentMark::in_progress() const { + return is_fully_initialized() ? _cm_thread->in_progress() : false; +} + PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const { return _partial_array_state_manager; } @@ -765,7 +774,7 @@ private: // as asserts here to minimize their overhead on the product. However, we // will have them as guarantees at the beginning / end of the bitmap // clearing to get some checking in the product. - assert(!suspendible() || _cm->cm_thread()->in_progress(), "invariant"); + assert(!suspendible() || _cm->in_progress(), "invariant"); assert(!suspendible() || !G1CollectedHeap::heap()->collector_state()->mark_or_rebuild_in_progress(), "invariant"); // Abort iteration if necessary. @@ -821,7 +830,8 @@ void G1ConcurrentMark::clear_bitmap(WorkerThreads* workers, bool may_yield) { void G1ConcurrentMark::cleanup_for_next_mark() { // Make sure that the concurrent mark thread looks to still be in // the current cycle. - guarantee(cm_thread()->in_progress(), "invariant"); + guarantee(is_fully_initialized(), "should be initializd"); + guarantee(in_progress(), "invariant"); // We are finishing up the current cycle by clearing the next // marking bitmap and getting it ready for the next cycle. During @@ -834,7 +844,8 @@ void G1ConcurrentMark::cleanup_for_next_mark() { reset_partial_array_state_manager(); // Repeat the asserts from above. - guarantee(cm_thread()->in_progress(), "invariant"); + guarantee(is_fully_initialized(), "should be initializd"); + guarantee(in_progress(), "invariant"); guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); } @@ -1925,7 +1936,8 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { // nothing, but this situation should be extremely rare (a full gc after shutdown // has been signalled is already rare), and this work should be negligible compared // to actual full gc work. - if (!cm_thread()->in_progress() && !_g1h->concurrent_mark_is_terminating()) { + + if (!is_fully_initialized() || (!cm_thread()->in_progress() && !_g1h->concurrent_mark_is_terminating())) { return false; } @@ -1987,6 +1999,10 @@ void G1ConcurrentMark::print_summary_info() { } log.trace(" Concurrent marking:"); + if (!is_fully_initialized()) { + log.trace(" has not been initialized yet"); + return; + } print_ms_time_info(" ", "remarks", _remark_times); { print_ms_time_info(" ", "final marks", _remark_mark_times); @@ -2003,7 +2019,10 @@ void G1ConcurrentMark::print_summary_info() { } void G1ConcurrentMark::threads_do(ThreadClosure* tc) const { - _concurrent_workers->threads_do(tc); + if (is_fully_initialized()) { // they are initialized late + tc->do_thread(_cm_thread); + _concurrent_workers->threads_do(tc); + } } void G1ConcurrentMark::print_on(outputStream* st) const { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 836d7793f81..367568aeff4 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -555,6 +555,9 @@ public: uint worker_id_offset() const { return _worker_id_offset; } + void fully_initialize(); + bool is_fully_initialized() const { return _cm_thread != nullptr; } + bool in_progress() const; uint max_num_tasks() const {return _max_num_tasks; } // Clear statistics gathered during the concurrent cycle for the given region after diff --git a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp index 50002ac2bfe..f280d76f3c7 100644 --- a/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp +++ b/src/hotspot/share/gc/g1/g1PeriodicGCTask.cpp @@ -39,7 +39,7 @@ bool G1PeriodicGCTask::should_start_periodic_gc(G1CollectedHeap* g1h, SuspendibleThreadSetJoiner sts; // If we are currently in a concurrent mark we are going to uncommit memory soon. - if (g1h->concurrent_mark()->cm_thread()->in_progress()) { + if (g1h->concurrent_mark()->in_progress()) { log_debug(gc, periodic)("Concurrent cycle in progress. Skipping."); return false; } diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 1d0b29c303e..98e6acc1d77 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -739,7 +739,7 @@ double G1Policy::constant_other_time_ms(double pause_time_ms) const { } bool G1Policy::about_to_start_mixed_phase() const { - return _g1h->concurrent_mark()->cm_thread()->in_progress() || collector_state()->in_young_gc_before_mixed(); + return _g1h->concurrent_mark()->in_progress() || collector_state()->in_young_gc_before_mixed(); } bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_word_size) { @@ -1235,7 +1235,7 @@ bool G1Policy::force_concurrent_start_if_outside_cycle(GCCause::Cause gc_cause) // We actually check whether we are marking here and not if we are in a // reclamation phase. This means that we will schedule a concurrent mark // even while we are still in the process of reclaiming memory. - bool during_cycle = _g1h->concurrent_mark()->cm_thread()->in_progress(); + bool during_cycle = _g1h->concurrent_mark()->in_progress(); if (!during_cycle) { log_debug(gc, ergo)("Request concurrent cycle initiation (requested by GC cause). " "GC cause: %s", diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp index d9b7ec294bd..c5f55e1d20c 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.cpp @@ -29,12 +29,12 @@ G1RegionMarkStatsCache::G1RegionMarkStatsCache(G1RegionMarkStats* target, uint num_cache_entries) : _target(target), + _cache(NEW_C_HEAP_ARRAY(G1RegionMarkStatsCacheEntry, num_cache_entries, mtGC)), _num_cache_entries(num_cache_entries), _num_cache_entries_mask(_num_cache_entries - 1) { guarantee(is_power_of_2(num_cache_entries), "Number of cache entries must be power of two, but is %u", num_cache_entries); - _cache = NEW_C_HEAP_ARRAY(G1RegionMarkStatsCacheEntry, _num_cache_entries, mtGC); } G1RegionMarkStatsCache::~G1RegionMarkStatsCache() { diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 1c024f2943b..56ab3a4b0fe 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -85,7 +85,7 @@ void VM_G1TryInitiateConcMark::doit() { GCCauseSetter x(g1h, _gc_cause); _mark_in_progress = g1h->collector_state()->mark_in_progress(); - _cycle_already_in_progress = g1h->concurrent_mark()->cm_thread()->in_progress(); + _cycle_already_in_progress = g1h->concurrent_mark()->in_progress(); if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) { // Failure to force the next GC pause to be a concurrent start indicates diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index de5bc9ea58f..35e0b83d25f 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -578,7 +578,7 @@ WB_END WB_ENTRY(jboolean, WB_G1InConcurrentMark(JNIEnv* env, jobject o)) if (UseG1GC) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - return g1h->concurrent_mark()->cm_thread()->in_progress(); + return g1h->concurrent_mark()->in_progress(); } THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_G1InConcurrentMark: G1 GC is not enabled"); WB_END From 1f3fd3da1d24118a29d28f01d3fa59d7712607e5 Mon Sep 17 00:00:00 2001 From: Anton Artemov Date: Mon, 2 Feb 2026 08:20:00 +0000 Subject: [PATCH 076/215] 8366659: ObjectMonitor::wait() liveness problem with a suspension request Co-authored-by: Patricio Chilano Mateo Co-authored-by: Daniel D. Daugherty Reviewed-by: dcubed, sspitsyn, dholmes, pchilanomate --- src/hotspot/share/runtime/objectMonitor.cpp | 226 ++++++----- src/hotspot/share/runtime/objectMonitor.hpp | 19 +- .../SuspendWithObjectMonitorWait.java | 371 ------------------ .../SuspendWithObjectMonitorWaitBase.java | 236 +++++++++++ .../SuspendWithObjectMonitorWaitDefault.java | 138 +++++++ ...WithObjectMonitorWaitReentryPartFirst.java | 152 +++++++ ...ithObjectMonitorWaitReentryPartSecond.java | 162 ++++++++ .../SuspendWithObjectMonitorWaitWorker.java | 128 ++++++ .../libSuspendWithObjectMonitorWait.cpp | 6 +- 9 files changed, 952 insertions(+), 486 deletions(-) delete mode 100644 test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitDefault.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartFirst.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartSecond.java create mode 100644 test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 785ee2af592..144533cd959 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -339,15 +339,6 @@ void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) { } } -void ObjectMonitor::ClearSuccOnSuspend::operator()(JavaThread* current) { - if (current->is_suspended()) { - if (_om->has_successor(current)) { - _om->clear_successor(); - OrderAccess::fence(); // always do a full fence when successor is cleared - } - } -} - #define assert_mark_word_consistency() \ assert(UseObjectMonitorTable || object()->mark() == markWord::encode(this), \ "object mark must match encoded this: mark=" INTPTR_FORMAT \ @@ -500,7 +491,7 @@ bool ObjectMonitor::spin_enter(JavaThread* current) { return false; } -bool ObjectMonitor::enter(JavaThread* current) { +bool ObjectMonitor::enter(JavaThread* current, bool post_jvmti_events) { assert(current == JavaThread::current(), "must be"); if (spin_enter(current)) { @@ -521,15 +512,15 @@ bool ObjectMonitor::enter(JavaThread* current) { } // At this point this ObjectMonitor cannot be deflated, finish contended enter - enter_with_contention_mark(current, contention_mark); + enter_with_contention_mark(current, contention_mark, post_jvmti_events); return true; } -void ObjectMonitor::notify_contended_enter(JavaThread* current) { +void ObjectMonitor::notify_contended_enter(JavaThread* current, bool post_jvmti_events) { current->set_current_pending_monitor(this); DTRACE_MONITOR_PROBE(contended__enter, this, object(), current); - if (JvmtiExport::should_post_monitor_contended_enter()) { + if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_enter()) { JvmtiExport::post_monitor_contended_enter(current, this); // The current thread does not yet own the monitor and does not @@ -540,7 +531,7 @@ void ObjectMonitor::notify_contended_enter(JavaThread* current) { } } -void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark &cm) { +void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark &cm, bool post_jvmti_events) { assert(current == JavaThread::current(), "must be"); assert(!has_owner(current), "must be"); assert(cm._monitor == this, "must be"); @@ -564,7 +555,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito ContinuationEntry* ce = current->last_continuation(); bool is_virtual = ce != nullptr && ce->is_virtual_thread(); if (is_virtual) { - notify_contended_enter(current); + notify_contended_enter(current, post_jvmti_events); result = Continuation::try_preempt(current, ce->cont_oop(current)); if (result == freeze_ok) { bool acquired = vthread_monitor_enter(current); @@ -573,7 +564,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito // _entry_list so cancel preemption. We will still go through the preempt stub // but instead of unmounting we will call thaw to continue execution. current->set_preemption_cancelled(true); - if (JvmtiExport::should_post_monitor_contended_entered()) { + if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_entered()) { // We are going to call thaw again after this and finish the VMTS // transition so no need to do it here. We will post the event there. current->set_contended_entered_monitor(this); @@ -610,7 +601,8 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito // states will still report that the thread is blocked trying to // acquire it. // If there is a suspend request, ExitOnSuspend will exit the OM - // and set the OM as pending. + // and set the OM as pending, the thread will not be reported as + // having "-locked" the monitor. } if (!eos.exited()) { // ExitOnSuspend did not exit the OM @@ -644,7 +636,7 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito // spinning we could increment JVMStat counters, etc. DTRACE_MONITOR_PROBE(contended__entered, this, object(), current); - if (JvmtiExport::should_post_monitor_contended_entered()) { + if (post_jvmti_events && JvmtiExport::should_post_monitor_contended_entered()) { JvmtiExport::post_monitor_contended_entered(current, this); // The current thread already owns the monitor and is not going to @@ -1102,11 +1094,10 @@ void ObjectMonitor::enter_internal(JavaThread* current) { void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentNode) { assert(current != nullptr, "invariant"); - assert(current->thread_state() != _thread_blocked, "invariant"); + assert(current->thread_state() == _thread_blocked, "invariant"); assert(currentNode != nullptr, "invariant"); assert(currentNode->_thread == current, "invariant"); assert(_waiters > 0, "invariant"); - assert_mark_word_consistency(); // If there are unmounted virtual threads ahead in the _entry_list we want // to do a timed-park instead to alleviate some deadlock cases where one @@ -1142,22 +1133,15 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN { OSThreadContendState osts(current->osthread()); - - assert(current->thread_state() == _thread_in_vm, "invariant"); - - { - ClearSuccOnSuspend csos(this); - ThreadBlockInVMPreprocess tbivs(current, csos, true /* allow_suspend */); - if (do_timed_parked) { - current->_ParkEvent->park(recheck_interval); - // Increase the recheck_interval, but clamp the value. - recheck_interval *= 8; - if (recheck_interval > MAX_RECHECK_INTERVAL) { - recheck_interval = MAX_RECHECK_INTERVAL; - } - } else { - current->_ParkEvent->park(); + if (do_timed_parked) { + current->_ParkEvent->park(recheck_interval); + // Increase the recheck_interval, but clamp the value. + recheck_interval *= 8; + if (recheck_interval > MAX_RECHECK_INTERVAL) { + recheck_interval = MAX_RECHECK_INTERVAL; } + } else { + current->_ParkEvent->park(); } } @@ -1184,7 +1168,6 @@ void ObjectMonitor::reenter_internal(JavaThread* current, ObjectWaiter* currentN // Current has acquired the lock -- Unlink current from the _entry_list. assert(has_owner(current), "invariant"); - assert_mark_word_consistency(); unlink_after_acquire(current, currentNode); if (has_successor(current)) clear_successor(); assert(!has_successor(current), "invariant"); @@ -1883,7 +1866,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // while (!timeout && !interrupted && node.TState == TS_WAIT) park() int ret = OS_OK; - bool was_notified = false; + bool was_notified = true; // Need to check interrupt state whilst still _thread_in_vm bool interrupted = interruptible && current->is_interrupted(false); @@ -1895,8 +1878,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { assert(current->thread_state() == _thread_in_vm, "invariant"); { - ClearSuccOnSuspend csos(this); - ThreadBlockInVMPreprocess tbivs(current, csos, true /* allow_suspend */); + ThreadBlockInVM tbivm(current, false /* allow_suspend */); if (interrupted || HAS_PENDING_EXCEPTION) { // Intentionally empty } else if (node.TState == ObjectWaiter::TS_WAIT) { @@ -1928,17 +1910,16 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { if (node.TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(&node); // unlink from wait_set node.TState = ObjectWaiter::TS_RUN; + was_notified = false; } } - - // The thread is now either on off-list (TS_RUN), + // The thread is now either off-list (TS_RUN), // or on the entry_list (TS_ENTER). // The Node's TState variable is stable from the perspective of this thread. // No other threads will asynchronously modify TState. guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant"); OrderAccess::loadload(); if (has_successor(current)) clear_successor(); - was_notified = node.TState == ObjectWaiter::TS_ENTER; // Reentry phase -- reacquire the monitor. // re-enter contended monitor after object.wait(). @@ -1947,27 +1928,19 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // although the raw address of the object may have changed. // (Don't cache naked oops over safepoints, of course). - // post monitor waited event. Note that this is past-tense, we are done waiting. - if (JvmtiExport::should_post_monitor_waited()) { - JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT); + // Post monitor waited event. Note that this is past-tense, we are done waiting. + // An event could have been enabled after notification, in this case + // a thread will have TS_ENTER state and posting the event may hit a suspension point. + // From a debugging perspective, it is more important to have no missing events. + if (interruptible && JvmtiExport::should_post_monitor_waited() && node.TState != ObjectWaiter::TS_ENTER) { - if (was_notified && has_successor(current)) { - // In this part of the monitor wait-notify-reenter protocol it - // is possible (and normal) for another thread to do a fastpath - // monitor enter-exit while this thread is still trying to get - // to the reenter portion of the protocol. - // - // The ObjectMonitor was notified and the current thread is - // the successor which also means that an unpark() has already - // been done. The JVMTI_EVENT_MONITOR_WAITED event handler can - // consume the unpark() that was done when the successor was - // set because the same ParkEvent is shared between Java - // monitors and JVM/TI RawMonitors (for now). - // - // We redo the unpark() to ensure forward progress, i.e., we - // don't want all pending threads hanging (parked) with none - // entering the unlocked monitor. - current->_ParkEvent->unpark(); + // Process suspend requests now if any, before posting the event. + { + ThreadBlockInVM tbvm(current, true); + } + // Re-check the condition as the monitor waited events can be disabled whilst thread was suspended. + if (JvmtiExport::should_post_monitor_waited()) { + JvmtiExport::post_monitor_waited(current, this, ret == OS_TIMEOUT); } } @@ -1986,8 +1959,30 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { NoPreemptMark npm(current); enter(current); } else { + // This means the thread has been un-parked and added to the entry_list + // in notify_internal, i.e. notified while waiting. guarantee(v == ObjectWaiter::TS_ENTER, "invariant"); - reenter_internal(current, &node); + ExitOnSuspend eos(this); + { + ThreadBlockInVMPreprocess tbivs(current, eos, true /* allow_suspend */); + reenter_internal(current, &node); + // We can go to a safepoint at the end of this block. If we + // do a thread dump during that safepoint, then this thread will show + // as having "-locked" the monitor, but the OS and java.lang.Thread + // states will still report that the thread is blocked trying to + // acquire it. + // If there is a suspend request, ExitOnSuspend will exit the OM + // and set the OM as pending, the thread will not be reported as + // having "-locked" the monitor. + } + if (eos.exited()) { + // ExitOnSuspend exit the OM + assert(!has_owner(current), "invariant"); + guarantee(node.TState == ObjectWaiter::TS_RUN, "invariant"); + current->set_current_pending_monitor(nullptr); + enter(current, false /* post_jvmti_events */); + } + assert(has_owner(current), "invariant"); node.wait_reenter_end(this); } @@ -2041,6 +2036,8 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { ObjectWaiter* iterator = dequeue_waiter(); if (iterator != nullptr) { guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant"); + iterator->_notifier_tid = JFR_THREAD_ID(current); + did_notify = true; if (iterator->is_vthread()) { oop vthread = iterator->vthread(); @@ -2056,45 +2053,55 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { old_state == java_lang_VirtualThread::TIMED_WAIT) { java_lang_VirtualThread::cmpxchg_state(vthread, old_state, java_lang_VirtualThread::BLOCKED); } - // Increment counter *before* adding the vthread to the _entry_list. - // Adding to _entry_list uses Atomic::cmpxchg() which already provides - // a fence that prevents reordering of the stores. - inc_unmounted_vthreads(); + if (!JvmtiExport::should_post_monitor_waited()) { + // Increment counter *before* adding the vthread to the _entry_list. + // Adding to _entry_list uses Atomic::cmpxchg() which already provides + // a fence that prevents reordering of the stores. + inc_unmounted_vthreads(); + add_to_entry_list(current, iterator); + } else { + iterator->TState = ObjectWaiter::TS_RUN; + if (java_lang_VirtualThread::set_onWaitingList(vthread, vthread_list_head())) { + ParkEvent* pe = ObjectMonitor::vthread_unparker_ParkEvent(); + pe->unpark(); + } + } + } else { + if (!JvmtiExport::should_post_monitor_waited()) { + add_to_entry_list(current, iterator); + // Read counter *after* adding the thread to the _entry_list. + // Adding to _entry_list uses Atomic::cmpxchg() which already provides + // a fence that prevents this load from floating up previous store. + if (has_unmounted_vthreads()) { + // Wake up the thread to alleviate some deadlock cases where the successor + // that will be picked up when this thread releases the monitor is an unmounted + // virtual thread that cannot run due to having run out of carriers. Upon waking + // up, the thread will call reenter_internal() which will use timed-park in case + // there is contention and there are still vthreads in the _entry_list. + // If the target was interrupted or the wait timed-out at the same time, it could + // have reached reenter_internal and read a false value of has_unmounted_vthreads() + // before we added it to the _entry_list above. To deal with that case, we set _do_timed_park + // which will be read by the target on the next loop iteration in reenter_internal. + iterator->_do_timed_park = true; + JavaThread* t = iterator->thread(); + t->_ParkEvent->unpark(); + } + iterator->wait_reenter_begin(this); + } else { + iterator->TState = ObjectWaiter::TS_RUN; + JavaThread* t = iterator->thread(); + assert(t != nullptr, ""); + t->_ParkEvent->unpark(); + } } - iterator->_notifier_tid = JFR_THREAD_ID(current); - did_notify = true; - add_to_entry_list(current, iterator); - // _wait_set_lock protects the wait queue, not the entry_list. We could // move the add-to-entry_list operation, above, outside the critical section // protected by _wait_set_lock. In practice that's not useful. With the - // exception of wait() timeouts and interrupts the monitor owner + // exception of wait() timeouts and interrupts the monitor owner // is the only thread that grabs _wait_set_lock. There's almost no contention // on _wait_set_lock so it's not profitable to reduce the length of the // critical section. - - if (!iterator->is_vthread()) { - iterator->wait_reenter_begin(this); - - // Read counter *after* adding the thread to the _entry_list. - // Adding to _entry_list uses Atomic::cmpxchg() which already provides - // a fence that prevents this load from floating up previous store. - if (has_unmounted_vthreads()) { - // Wake up the thread to alleviate some deadlock cases where the successor - // that will be picked up when this thread releases the monitor is an unmounted - // virtual thread that cannot run due to having run out of carriers. Upon waking - // up, the thread will call reenter_internal() which will use timed-park in case - // there is contention and there are still vthreads in the _entry_list. - // If the target was interrupted or the wait timed-out at the same time, it could - // have reached reenter_internal and read a false value of has_unmounted_vthreads() - // before we added it to the _entry_list above. To deal with that case, we set _do_timed_park - // which will be read by the target on the next loop iteration in reenter_internal. - iterator->_do_timed_park = true; - JavaThread* t = iterator->thread(); - t->_ParkEvent->unpark(); - } - } } return did_notify; } @@ -2221,19 +2228,22 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node // The first time we run after being preempted on Object.wait() we // need to check if we were interrupted or the wait timed-out, and // in that case remove ourselves from the _wait_set queue. + bool was_notified = true; if (node->TState == ObjectWaiter::TS_WAIT) { SpinCriticalSection scs(&_wait_set_lock); if (node->TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(node); // unlink from wait_set node->TState = ObjectWaiter::TS_RUN; + was_notified = false; } } // If this was an interrupted case, set the _interrupted boolean so that // once we re-acquire the monitor we know if we need to throw IE or not. ObjectWaiter::TStates state = node->TState; - bool was_notified = state == ObjectWaiter::TS_ENTER; - assert(was_notified || state == ObjectWaiter::TS_RUN, ""); + assert(was_notified || state == ObjectWaiter::TS_RUN, + "was not notified and is not in the right state: state = %s", + node->getTStateName(state)); node->_interrupted = node->_interruptible && !was_notified && current->is_interrupted(false); // Post JFR and JVMTI events. If non-interruptible we are in @@ -2246,7 +2256,10 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node // Mark that we are at reenter so that we don't call this method again. node->_at_reenter = true; - if (!was_notified) { + // We check the state rather than was_notified because, when JVMTI + // monitor_waited event is enabled, the notifier only unparks the waiter + // without adding it to the entry_list. + if (state == ObjectWaiter::TS_RUN) { bool acquired = vthread_monitor_enter(current, node); if (acquired) { guarantee(_recursions == 0, "invariant"); @@ -2537,6 +2550,23 @@ ObjectWaiter::ObjectWaiter(JavaThread* current) { _active = false; } +const char* ObjectWaiter::getTStateName(ObjectWaiter::TStates state) { + switch (state) { + case ObjectWaiter::TS_UNDEF: + return "TS_UNDEF"; + case ObjectWaiter::TS_READY: + return "TS_READY"; + case ObjectWaiter::TS_RUN: + return "TS_RUN"; + case ObjectWaiter::TS_WAIT: + return "TS_WAIT"; + case ObjectWaiter::TS_ENTER: + return "TS_ENTER"; + default: + ShouldNotReachHere(); + } +} + ObjectWaiter::ObjectWaiter(oop vthread, ObjectMonitor* mon) : ObjectWaiter(nullptr) { assert(oopDesc::is_oop(vthread), ""); _vthread = OopHandle(JavaThread::thread_oop_storage(), vthread); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 53b64f1e8a5..574a652f230 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,6 @@ class ParkEvent; class BasicLock; class ContinuationWrapper; - class ObjectWaiter : public CHeapObj { public: enum TStates : uint8_t { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER }; @@ -72,7 +71,7 @@ class ObjectWaiter : public CHeapObj { oop vthread() const; void wait_reenter_begin(ObjectMonitor *mon); void wait_reenter_end(ObjectMonitor *mon); - + const char* getTStateName(TStates state); void set_bad_pointers() { #ifdef ASSERT this->_prev = (ObjectWaiter*) badAddressVal; @@ -352,7 +351,6 @@ class ObjectMonitor : public CHeapObj { // returns false and throws IllegalMonitorStateException (IMSE). bool check_owner(TRAPS); - private: class ExitOnSuspend { protected: ObjectMonitor* _om; @@ -362,23 +360,16 @@ class ObjectMonitor : public CHeapObj { void operator()(JavaThread* current); bool exited() { return _om_exited; } }; - class ClearSuccOnSuspend { - protected: - ObjectMonitor* _om; - public: - ClearSuccOnSuspend(ObjectMonitor* om) : _om(om) {} - void operator()(JavaThread* current); - }; bool enter_is_async_deflating(); - void notify_contended_enter(JavaThread *current); + void notify_contended_enter(JavaThread *current, bool post_jvmti_events = true); public: void enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark); bool enter_for(JavaThread* locking_thread); - bool enter(JavaThread* current); + bool enter(JavaThread* current, bool post_jvmti_events = true); bool try_enter(JavaThread* current, bool check_for_recursion = true); bool spin_enter(JavaThread* current); - void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark); + void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark, bool post_jvmti_events = true); void exit(JavaThread* current, bool not_suspended = true); bool resume_operation(JavaThread* current, ObjectWaiter* node, ContinuationWrapper& cont); void wait(jlong millis, bool interruptible, TRAPS); diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java deleted file mode 100644 index 3a747a3e86b..00000000000 --- a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWait.java +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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 4413752 8262881 - * @summary Test SuspendThread with ObjectMonitor wait. - * @requires vm.jvmti - * @library /test/lib - * @compile SuspendWithObjectMonitorWait.java - * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWait - */ - -import java.io.PrintStream; - -// -// main waiter resumer -// ================= ================== =================== -// launch waiter -// waiter running -// launch resumer enter threadLock -// threadLock.wait() resumer running -// enter threadLock : wait for notify -// threadLock.notify wait finishes : -// : reenter blocks : -// suspend waiter : -// exit threadLock : : -// : : -// : : : -// notify resumer : wait finishes -// join resumer : enter threadLock -// : resume waiter -// : : exit threadLock -// : reenter threadLock : -// : resumer exits -// join waiter : -// waiter exits -// - -public class SuspendWithObjectMonitorWait { - private static final String AGENT_LIB = "SuspendWithObjectMonitorWait"; - private static final int exit_delta = 95; - - private static final int DEF_TIME_MAX = 60; // default max # secs to test - private static final int JOIN_MAX = 30; // max # secs to wait for join - - public static final int TS_INIT = 1; // initial testState - public static final int TS_WAITER_RUNNING = 2; // waiter is running - public static final int TS_RESUMER_RUNNING = 3; // resumer is running - public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock - public static final int TS_CALL_SUSPEND = 5; // call suspend on contender - public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter - public static final int TS_CALL_RESUME = 7; // call resume on waiter - public static final int TS_WAITER_DONE = 8; // waiter has run; done - - public static Object barrierLaunch = new Object(); // controls thread launch - public static Object barrierResumer = new Object(); // controls resumer - public static Object threadLock = new Object(); // testing object - - public static long count = 0; - public static boolean printDebug = false; - public volatile static int testState; - - private static void log(String msg) { System.out.println(msg); } - - native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr); - native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr); - - public static void main(String[] args) throws Exception { - try { - System.loadLibrary(AGENT_LIB); - log("Loaded library: " + AGENT_LIB); - } catch (UnsatisfiedLinkError ule) { - log("Failed to load library: " + AGENT_LIB); - log("java.library.path: " + System.getProperty("java.library.path")); - throw ule; - } - - int timeMax = 0; - if (args.length == 0) { - timeMax = DEF_TIME_MAX; - } else { - int argIndex = 0; - int argsLeft = args.length; - if (args[0].equals("-p")) { - printDebug = true; - argIndex = 1; - argsLeft--; - } - if (argsLeft == 0) { - timeMax = DEF_TIME_MAX; - } else if (argsLeft == 1) { - try { - timeMax = Integer.parseUnsignedInt(args[argIndex]); - } catch (NumberFormatException nfe) { - System.err.println("'" + args[argIndex] + - "': invalid timeMax value."); - usage(); - } - } else { - usage(); - } - } - - System.exit(run(timeMax, System.out) + exit_delta); - } - - public static void logDebug(String mesg) { - if (printDebug) { - System.err.println(Thread.currentThread().getName() + ": " + mesg); - } - } - - public static void usage() { - System.err.println("Usage: " + AGENT_LIB + " [-p][time_max]"); - System.err.println("where:"); - System.err.println(" -p ::= print debug info"); - System.err.println(" time_max ::= max looping time in seconds"); - System.err.println(" (default is " + DEF_TIME_MAX + - " seconds)"); - System.exit(1); - } - - public static int run(int timeMax, PrintStream out) { - return (new SuspendWithObjectMonitorWait()).doWork(timeMax, out); - } - - public static void checkTestState(int exp) { - if (testState != exp) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("Unexpected test state value: " - + "expected=" + exp + " actual=" + testState); - } - } - - public int doWork(int timeMax, PrintStream out) { - SuspendWithObjectMonitorWaitWorker waiter; // waiter thread - SuspendWithObjectMonitorWaitWorker resumer; // resumer thread - - System.out.println("About to execute for " + timeMax + " seconds."); - - long start_time = System.currentTimeMillis(); - while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { - count++; - testState = TS_INIT; // starting the test loop - - // launch the waiter thread - synchronized (barrierLaunch) { - waiter = new SuspendWithObjectMonitorWaitWorker("waiter"); - waiter.start(); - - while (testState != TS_WAITER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - // launch the resumer thread - synchronized (barrierLaunch) { - resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); - resumer.start(); - - while (testState != TS_RESUMER_RUNNING) { - try { - barrierLaunch.wait(0); // wait until it is running - } catch (InterruptedException ex) { - } - } - } - - checkTestState(TS_RESUMER_RUNNING); - - // The waiter thread was synchronized on threadLock before it - // set TS_WAITER_RUNNING and notified barrierLaunch above so - // we cannot enter threadLock until the waiter thread calls - // threadLock.wait(). - synchronized (threadLock) { - // notify waiter thread so it can try to reenter threadLock - testState = TS_READY_TO_NOTIFY; - threadLock.notify(); - - // wait for the waiter thread to block - logDebug("before contended enter wait"); - int retCode = wait4ContendedEnter(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI GetThreadState: " - + "retCode=" + retCode); - } - logDebug("done contended enter wait"); - - checkTestState(TS_READY_TO_NOTIFY); - testState = TS_CALL_SUSPEND; - logDebug("before suspend thread"); - retCode = suspendThread(waiter); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI SuspendThread: " - + "retCode=" + retCode); - } - logDebug("suspended thread"); - } - - // - // At this point, all of the child threads are running - // and we can get to meat of the test: - // - // - suspended threadLock waiter (trying to reenter) - // - a threadLock enter in the resumer thread - // - resumption of the waiter thread - // - a threadLock enter in the freshly resumed waiter thread - // - - synchronized (barrierResumer) { - checkTestState(TS_CALL_SUSPEND); - - // tell resumer thread to resume waiter thread - testState = TS_READY_TO_RESUME; - barrierResumer.notify(); - - // Can't call checkTestState() here because the - // resumer thread may have already resumed the - // waiter thread. - } - - try { - resumer.join(JOIN_MAX * 1000); - if (resumer.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("resumer thread is stuck"); - } - waiter.join(JOIN_MAX * 1000); - if (waiter.isAlive()) { - System.err.println("Failure at " + count + " loops."); - throw new InternalError("waiter thread is stuck"); - } - } catch (InterruptedException ex) { - } - - checkTestState(TS_WAITER_DONE); - } - - System.out.println("Executed " + count + " loops in " + timeMax + - " seconds."); - - return 0; - } -} - -class SuspendWithObjectMonitorWaitWorker extends Thread { - private SuspendWithObjectMonitorWaitWorker target; // target for resume operation - - public SuspendWithObjectMonitorWaitWorker(String name) { - super(name); - } - - public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) { - super(name); - this.target = target; - } - - native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr); - - public void run() { - SuspendWithObjectMonitorWait.logDebug("thread running"); - - // - // Launch the waiter thread: - // - grab the threadLock - // - threadLock.wait() - // - releases threadLock - // - if (getName().equals("waiter")) { - // grab threadLock before we tell main we are running - SuspendWithObjectMonitorWait.logDebug("before enter threadLock"); - synchronized(SuspendWithObjectMonitorWait.threadLock) { - SuspendWithObjectMonitorWait.logDebug("enter threadLock"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_INIT); - - synchronized(SuspendWithObjectMonitorWait.barrierLaunch) { - // tell main we are running - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_RUNNING; - SuspendWithObjectMonitorWait.barrierLaunch.notify(); - } - - SuspendWithObjectMonitorWait.logDebug("before wait"); - - // TS_READY_TO_NOTIFY is set after the main thread has - // entered threadLock so a spurious wakeup can't get the - // waiter thread out of this threadLock.wait(0) call: - while (SuspendWithObjectMonitorWait.testState <= SuspendWithObjectMonitorWait.TS_READY_TO_NOTIFY) { - try { - SuspendWithObjectMonitorWait.threadLock.wait(0); - } catch (InterruptedException ex) { - } - } - - SuspendWithObjectMonitorWait.logDebug("after wait"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_CALL_RESUME); - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_WAITER_DONE; - - SuspendWithObjectMonitorWait.logDebug("exit threadLock"); - } - } - // - // Launch the resumer thread: - // - tries to grab the threadLock (should not block!) - // - grabs threadLock - // - resumes the waiter thread - // - releases threadLock - // - else if (getName().equals("resumer")) { - synchronized(SuspendWithObjectMonitorWait.barrierResumer) { - synchronized(SuspendWithObjectMonitorWait.barrierLaunch) { - // tell main we are running - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_RESUMER_RUNNING; - SuspendWithObjectMonitorWait.barrierLaunch.notify(); - } - SuspendWithObjectMonitorWait.logDebug("thread waiting"); - while (SuspendWithObjectMonitorWait.testState != SuspendWithObjectMonitorWait.TS_READY_TO_RESUME) { - try { - // wait for main to tell us when to continue - SuspendWithObjectMonitorWait.barrierResumer.wait(0); - } catch (InterruptedException ex) { - } - } - } - - SuspendWithObjectMonitorWait.logDebug("before enter threadLock"); - synchronized(SuspendWithObjectMonitorWait.threadLock) { - SuspendWithObjectMonitorWait.logDebug("enter threadLock"); - - SuspendWithObjectMonitorWait.checkTestState(SuspendWithObjectMonitorWait.TS_READY_TO_RESUME); - SuspendWithObjectMonitorWait.testState = SuspendWithObjectMonitorWait.TS_CALL_RESUME; - - // resume the waiter thread so waiter.join() can work - SuspendWithObjectMonitorWait.logDebug("before resume thread"); - int retCode = resumeThread(target); - if (retCode != 0) { - throw new RuntimeException("error in JVMTI ResumeThread: " + - "retCode=" + retCode); - } - SuspendWithObjectMonitorWait.logDebug("resumed thread"); - - SuspendWithObjectMonitorWait.logDebug("exit threadLock"); - } - } - } -} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java new file mode 100644 index 00000000000..5443e4005fe --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitBase.java @@ -0,0 +1,236 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.PrintStream; + +public class SuspendWithObjectMonitorWaitBase { + protected static final String AGENT_LIB = "SuspendWithObjectMonitorWait"; + protected static final int exit_delta = 95; + + protected static final int DEF_TIME_MAX = 60; // default max # secs to test + protected static final int JOIN_MAX = 30; // max # secs to wait for join + + public static final int TS_INIT = 1; // initial testState + public static final int TS_WAITER_RUNNING = 2; // waiter is running + public static final int TS_RESUMER_RUNNING = 3; // resumer is running + public static final int TS_READY_TO_NOTIFY = 4; // ready to notify threadLock + public static final int TS_CALL_SUSPEND = 5; // call suspend on contender + public static final int TS_READY_TO_RESUME = 6; // ready to resume waiter + public static final int TS_CALL_RESUME = 7; // call resume on waiter + public static final int TS_WAITER_DONE = 8; // waiter has run; done + + public static Object barrierLaunch = new Object(); // controls thread launch + public static Object barrierResumer = new Object(); // controls resumer + public static Object threadLock = new Object(); // testing object + + public static long count = 0; + public static boolean printDebug = false; + public volatile static int testState; + + protected static void log(String msg) { System.out.println(msg); } + + native static int suspendThread(SuspendWithObjectMonitorWaitWorker thr); + native static int wait4ContendedEnter(SuspendWithObjectMonitorWaitWorker thr); + + public static void logDebug(String mesg) { + if (printDebug) { + System.err.println(Thread.currentThread().getName() + ": " + mesg); + } + } + + public static void usage() { + System.err.println("Usage: " + AGENT_LIB + " test_case [-p] [time_max]"); + System.err.println("where:"); + System.err.println(" test_case ::= 1 | 2 | 3"); + System.err.println(" -p ::= print debug info"); + System.err.println(" time_max ::= max looping time in seconds"); + System.err.println(" (default is " + DEF_TIME_MAX + + " seconds)"); + System.exit(1); + } + + public static void checkTestState(int exp) { + if (testState != exp) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("Unexpected test state value: " + + "expected=" + exp + " actual=" + testState); + } + } + + public SuspendWithObjectMonitorWaitWorker launchWaiter(long waitTimeout) { + SuspendWithObjectMonitorWaitWorker waiter; + // launch the waiter thread + synchronized (barrierLaunch) { + waiter = new SuspendWithObjectMonitorWaitWorker("waiter", waitTimeout); + waiter.start(); + + while (testState != TS_WAITER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + return waiter; + } + + public SuspendWithObjectMonitorWaitWorker launchResumer(SuspendWithObjectMonitorWaitWorker waiter) { + SuspendWithObjectMonitorWaitWorker resumer; + synchronized (barrierLaunch) { + resumer = new SuspendWithObjectMonitorWaitWorker("resumer", waiter); + resumer.start(); + + while (testState != TS_RESUMER_RUNNING) { + try { + barrierLaunch.wait(0); // wait until it is running + } catch (InterruptedException ex) { + } + } + } + return resumer; + } + + public void barrierResumerNotify() { + synchronized (barrierResumer) { + checkTestState(TS_CALL_SUSPEND); + + // tell resumer thread to resume waiter thread + testState = TS_READY_TO_RESUME; + barrierResumer.notify(); + + // Can't call checkTestState() here because the + // resumer thread may have already resumed the + // waiter thread. + } + } + + public void shutDown(SuspendWithObjectMonitorWaitWorker resumer, SuspendWithObjectMonitorWaitWorker waiter) { + try { + resumer.join(JOIN_MAX * 1000); + if (resumer.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("resumer thread is stuck"); + } + waiter.join(JOIN_MAX * 1000); + if (waiter.isAlive()) { + System.err.println("Failure at " + count + " loops."); + throw new InternalError("waiter thread is stuck"); + } + } catch (InterruptedException ex) { + } + } + + public int run(int timeMax, PrintStream out) { + return 0; + } + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + System.err.println("Invalid number of arguments, there should be at least a test_case given."); + usage(); + } + + if (args.length > 3) { + System.err.println("Invalid number of arguments, there are too many arguments."); + usage(); + } + + try { + System.loadLibrary(AGENT_LIB); + log("Loaded library: " + AGENT_LIB); + } catch (UnsatisfiedLinkError ule) { + log("Failed to load library: " + AGENT_LIB); + log("java.library.path: " + System.getProperty("java.library.path")); + throw ule; + } + + int testCase = 0; + int timeMax = 0; + for (int argIndex = 0; argIndex < args.length; argIndex++) { + if (args[argIndex].equals("-p")) { + // Handle optional -p arg regardless of position. + printDebug = true; + continue; + } + + if (testCase == 0) { + try { + // testCase must be the first non-optional arg. + testCase = Integer.parseUnsignedInt(args[argIndex]); + log("testCase = " + testCase); + } catch (NumberFormatException nfe) { + System.err.println("'" + args[argIndex] + + "': invalid test_case value."); + usage(); + } + if (testCase < 1 || testCase > 3) { + System.err.println("Invalid test_case value: '" + testCase + "'"); + usage(); + } + continue; + } + + if (argIndex < args.length) { + // timeMax is an optional arg. + try { + timeMax = Integer.parseUnsignedInt(args[argIndex]); + } catch (NumberFormatException nfe) { + System.err.println("'" + args[argIndex] + + "': invalid time_max value."); + usage(); + } + } else { + timeMax = DEF_TIME_MAX; + } + } + + if (timeMax == 0) { + timeMax = DEF_TIME_MAX; + } + log("timeMax = " + timeMax); + + if (testCase == 0) { + // Just -p was given. + System.err.println("Invalid number of arguments, no test_case given."); + usage(); + } + + SuspendWithObjectMonitorWaitBase test = null; + switch (testCase) { + case 1: + test = new SuspendWithObjectMonitorWaitDefault(); + break; + case 2: + test = new SuspendWithObjectMonitorWaitReentryPartFirst(); + break; + case 3: + test = new SuspendWithObjectMonitorWaitReentryPartSecond(); + break; + default: + // Impossible + break; + } + int result = test.run(timeMax, System.out); + System.exit(result + exit_delta); + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitDefault.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitDefault.java new file mode 100644 index 00000000000..b2c1f108de5 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitDefault.java @@ -0,0 +1,138 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWaitDefault.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitDefault 1 + */ + +import java.io.PrintStream; + +// +// SuspendWithObjectMonitorWaitDefault algorithm: +// +// main waiter resumer +// ================= ================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// threadLock.wait() resumer running +// enter threadLock : wait for notify +// threadLock.notify wait finishes : +// : reenter blocks : +// suspend waiter : +// exit threadLock : : +// : : +// : : : +// notify resumer : wait finishes +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// + +public class SuspendWithObjectMonitorWaitDefault extends SuspendWithObjectMonitorWaitBase { + + @Override + public int run(int timeMax, PrintStream out) { + return doWork1(timeMax, out); + } + + // Default scenario, the resumer thread is always able to grab the threadLock once notified by the main thread. + public int doWork1(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("Test 1: About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + waiter = launchWaiter(0); + + // launch the resumer thread + resumer = launchResumer(waiter); + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + } + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a threadLock enter in the resumer thread + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + barrierResumerNotify(); + + shutDown(waiter ,resumer); + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartFirst.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartFirst.java new file mode 100644 index 00000000000..b331b338f47 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartFirst.java @@ -0,0 +1,152 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWaitReentryPartFirst.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitReentryPartFirst 2 + */ + +import java.io.PrintStream; + +// +// SuspendWithObjectMonitorWaitReentryPartFirst algorithm: +// +// main waiter resumer +// ================= ================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// threadLock.wait() resumer running +// enter threadLock : wait for notify +// threadLock.notify wait finishes : +// : reenter blocks : +// suspend waiter : +// : : +// : : : +// notify resumer : wait finishes +// delay 1-second : : +// exit threadLock : : +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// +// Note: The sleep(1-second) in main along with the delayed exit +// of threadLock in main forces the resumer thread to reach +// "enter threadLock" and block. This difference from the default scenario +// forces the resumer thread to be contending for threadLock +// while the waiter thread is in threadLock.wait() increasing +// stress on the monitor sub-system. +// + +public class SuspendWithObjectMonitorWaitReentryPartFirst extends SuspendWithObjectMonitorWaitBase { + + @Override + public int run(int timeMax, PrintStream out) { + return doWork2(timeMax, out); + } + + // Notify the resumer while holding the threadLock. + public int doWork2(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("Test 2: About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + waiter = launchWaiter(0); + + // launch the resumer thread + resumer = launchResumer(waiter); + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a blocked threadLock enter in the resumer thread while the + // threadLock is held by the main thread. + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + barrierResumerNotify(); + try { + // Delay for 1-second while holding the threadLock to force the + // resumer thread to block on entering the threadLock. + Thread.sleep(1000); + } catch (Exception e) {} + } + + shutDown(waiter ,resumer); + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartSecond.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartSecond.java new file mode 100644 index 00000000000..92ab5a88eb6 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitReentryPartSecond.java @@ -0,0 +1,162 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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 4413752 8262881 + * @summary Test SuspendThread with ObjectMonitor wait. + * @requires vm.jvmti + * @library /test/lib + * @compile SuspendWithObjectMonitorWaitReentryPartSecond.java + * @run main/othervm/native -agentlib:SuspendWithObjectMonitorWait SuspendWithObjectMonitorWaitReentryPartSecond 3 + */ + +import java.io.PrintStream; + +// +// SuspendWithObjectMonitorWaitReentryPartSecond algorithm: +// +// main waiter resumer +// =================== ====================== =================== +// launch waiter +// waiter running +// launch resumer enter threadLock +// while !READY_TO_NOTIFY resumer running +// : threadLock.wait(1) wait for notify +// enter threadLock : : +// set READY_TO_NOTIFY : +// threadLock.notify wait finishes : +// : delay 200ms reenter blocks : +// suspend waiter : +// : : +// : : : +// notify resumer : wait finishes +// delay 1-second : : +// exit threadLock : : +// join resumer : enter threadLock +// : resume waiter +// : : exit threadLock +// : reenter threadLock : +// : resumer exits +// join waiter : +// waiter exits +// +// Note: The sleep(1-second) in main along with the delayed exit +// of threadLock in main forces the resumer thread to reach +// "enter threadLock" and block. This difference from the default scenario +// forces the resumer thread to be contending for threadLock +// while the waiter thread is in the threadLock.wait(1) tight +// loop increasing stress on the monitor sub-system. +// +// Note: sleep(200ms) here while holding the threadLock to allow the +// waiter thread's timed wait to finish before we attempt to +// suspend the waiter thread. +// + +public class SuspendWithObjectMonitorWaitReentryPartSecond extends SuspendWithObjectMonitorWaitBase { + + @Override + public int run(int timeMax, PrintStream out) { + return doWork3(timeMax, out); + } + + // Suspend on the re-entry path of wait. + public int doWork3(int timeMax, PrintStream out) { + SuspendWithObjectMonitorWaitWorker waiter; // waiter thread + SuspendWithObjectMonitorWaitWorker resumer; // resumer thread + + System.out.println("Test 3: About to execute for " + timeMax + " seconds."); + + long start_time = System.currentTimeMillis(); + while (System.currentTimeMillis() < start_time + (timeMax * 1000)) { + count++; + testState = TS_INIT; // starting the test loop + + // launch the waiter thread + waiter = launchWaiter(100); + + // launch the resumer thread + resumer = launchResumer(waiter); + + checkTestState(TS_RESUMER_RUNNING); + + // The waiter thread was synchronized on threadLock before it + // set TS_WAITER_RUNNING and notified barrierLaunch above so + // we cannot enter threadLock until the waiter thread calls + // threadLock.wait(). + synchronized (threadLock) { + // notify waiter thread so it can try to reenter threadLock + testState = TS_READY_TO_NOTIFY; + threadLock.notify(); + + try { + Thread.sleep(200); + } catch (Exception e) {} + + // wait for the waiter thread to block + logDebug("before contended enter wait"); + int retCode = wait4ContendedEnter(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI GetThreadState: " + + "retCode=" + retCode); + } + logDebug("done contended enter wait"); + + checkTestState(TS_READY_TO_NOTIFY); + testState = TS_CALL_SUSPEND; + logDebug("before suspend thread"); + retCode = suspendThread(waiter); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI SuspendThread: " + + "retCode=" + retCode); + } + logDebug("suspended thread"); + + // + // At this point, all of the child threads are running + // and we can get to meat of the test: + // + // - suspended threadLock waiter (trying to reenter) + // - a blocked threadLock enter in the resumer thread while the + // threadLock is held by the main thread. + // - resumption of the waiter thread + // - a threadLock enter in the freshly resumed waiter thread + // + barrierResumerNotify(); + try { + // Delay for 1-second while holding the threadLock to force the + // resumer thread to block on entering the threadLock. + Thread.sleep(1000); + } catch (Exception e) {} + } + + shutDown(waiter ,resumer); + checkTestState(TS_WAITER_DONE); + } + + System.out.println("Executed " + count + " loops in " + timeMax + + " seconds."); + + return 0; + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java new file mode 100644 index 00000000000..2bf41168d5a --- /dev/null +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/SuspendWithObjectMonitorWaitWorker.java @@ -0,0 +1,128 @@ +/* + * 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public class SuspendWithObjectMonitorWaitWorker extends Thread { + private SuspendWithObjectMonitorWaitWorker target; // target for resume operation + private final long waitTimeout; + + public SuspendWithObjectMonitorWaitWorker(String name, long waitTimeout) { + super(name); + this.waitTimeout = waitTimeout; + } + + public SuspendWithObjectMonitorWaitWorker(String name, SuspendWithObjectMonitorWaitWorker target) { + super(name); + this.target = target; + this.waitTimeout = 0; + } + + native static int resumeThread(SuspendWithObjectMonitorWaitWorker thr); + + public void run() { + SuspendWithObjectMonitorWaitBase.logDebug("thread running"); + + // + // Launch the waiter thread: + // - grab the threadLock + // - threadLock.wait() + // - releases threadLock + // + if (getName().equals("waiter")) { + // grab threadLock before we tell main we are running + SuspendWithObjectMonitorWaitBase.logDebug("before enter threadLock"); + synchronized(SuspendWithObjectMonitorWaitBase.threadLock) { + SuspendWithObjectMonitorWaitBase.logDebug("enter threadLock"); + + SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_INIT); + + synchronized(SuspendWithObjectMonitorWaitBase.barrierLaunch) { + // tell main we are running + SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_WAITER_RUNNING; + SuspendWithObjectMonitorWaitBase.barrierLaunch.notify(); + } + + SuspendWithObjectMonitorWaitBase.logDebug("before wait"); + + // TS_READY_TO_NOTIFY is set after the main thread has + // entered threadLock so a spurious wakeup can't get the + // waiter thread out of this threadLock.wait(0) call: + while (SuspendWithObjectMonitorWaitBase.testState <= SuspendWithObjectMonitorWaitBase.TS_READY_TO_NOTIFY) { + try { + SuspendWithObjectMonitorWaitBase.threadLock.wait(waitTimeout); + } catch (InterruptedException ex) { + } + } + + SuspendWithObjectMonitorWaitBase.logDebug("after wait"); + + SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_CALL_RESUME); + SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_WAITER_DONE; + + SuspendWithObjectMonitorWaitBase.logDebug("exit threadLock"); + } + } + // + // Launch the resumer thread: + // - tries to grab the threadLock (should not block with doWork1!) + // - grabs threadLock + // - resumes the waiter thread + // - releases threadLock + // + else if (getName().equals("resumer")) { + synchronized(SuspendWithObjectMonitorWaitBase.barrierResumer) { + synchronized(SuspendWithObjectMonitorWaitBase.barrierLaunch) { + // tell main we are running + SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_RESUMER_RUNNING; + SuspendWithObjectMonitorWaitBase.barrierLaunch.notify(); + } + SuspendWithObjectMonitorWaitBase.logDebug("thread waiting"); + while (SuspendWithObjectMonitorWaitBase.testState != SuspendWithObjectMonitorWaitBase.TS_READY_TO_RESUME) { + try { + // wait for main to tell us when to continue + SuspendWithObjectMonitorWaitBase.barrierResumer.wait(0); + } catch (InterruptedException ex) { + } + } + } + + SuspendWithObjectMonitorWaitBase.logDebug("before enter threadLock"); + synchronized(SuspendWithObjectMonitorWaitBase.threadLock) { + SuspendWithObjectMonitorWaitBase.logDebug("enter threadLock"); + + SuspendWithObjectMonitorWaitBase.checkTestState(SuspendWithObjectMonitorWaitBase.TS_READY_TO_RESUME); + SuspendWithObjectMonitorWaitBase.testState = SuspendWithObjectMonitorWaitBase.TS_CALL_RESUME; + + // resume the waiter thread so waiter.join() can work + SuspendWithObjectMonitorWaitBase.logDebug("before resume thread"); + int retCode = resumeThread(target); + if (retCode != 0) { + throw new RuntimeException("error in JVMTI ResumeThread: " + + "retCode=" + retCode); + } + SuspendWithObjectMonitorWaitBase.logDebug("resumed thread"); + + SuspendWithObjectMonitorWaitBase.logDebug("exit threadLock"); + } + } + } +} diff --git a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp index 3706cba76dc..bea70c4925f 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/SuspendWithObjectMonitorWait/libSuspendWithObjectMonitorWait.cpp @@ -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 @@ -36,7 +36,7 @@ static jvmtiEnv* jvmti = nullptr; } while (0) JNIEXPORT int JNICALL -Java_SuspendWithObjectMonitorWait_suspendThread(JNIEnv *jni, jclass cls, jthread thr) { +Java_SuspendWithObjectMonitorWaitBase_suspendThread(JNIEnv *jni, jclass cls, jthread thr) { return jvmti->SuspendThread(thr); } @@ -46,7 +46,7 @@ Java_SuspendWithObjectMonitorWaitWorker_resumeThread(JNIEnv *jni, jclass cls, jt } JNIEXPORT jint JNICALL -Java_SuspendWithObjectMonitorWait_wait4ContendedEnter(JNIEnv *jni, jclass cls, jthread thr) { +Java_SuspendWithObjectMonitorWaitBase_wait4ContendedEnter(JNIEnv *jni, jclass cls, jthread thr) { jvmtiError err; jint thread_state; do { From 5e248603813a46221c97f1c05311b06f21387bd7 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 2 Feb 2026 09:59:40 +0000 Subject: [PATCH 077/215] 8376115: G1: Convert G1CMRootRegions to use Atomic Reviewed-by: kbarrett, iwalulya --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 32 +++++++++++--------- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 12 ++++---- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 29de5a12599..5f096c2b9d7 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -382,12 +382,12 @@ G1CMRootMemRegions::~G1CMRootMemRegions() { } void G1CMRootMemRegions::reset() { - _num_root_regions = 0; + _num_root_regions.store_relaxed(0); } void G1CMRootMemRegions::add(HeapWord* start, HeapWord* end) { assert_at_safepoint(); - size_t idx = AtomicAccess::fetch_then_add(&_num_root_regions, 1u); + size_t idx = _num_root_regions.fetch_then_add(1u); assert(idx < _max_regions, "Trying to add more root MemRegions than there is space %zu", _max_regions); assert(start != nullptr && end != nullptr && start <= end, "Start (" PTR_FORMAT ") should be less or equal to " "end (" PTR_FORMAT ")", p2i(start), p2i(end)); @@ -398,36 +398,38 @@ void G1CMRootMemRegions::add(HeapWord* start, HeapWord* end) { void G1CMRootMemRegions::prepare_for_scan() { assert(!scan_in_progress(), "pre-condition"); - _scan_in_progress = _num_root_regions > 0; + _scan_in_progress.store_relaxed(num_root_regions() > 0); - _claimed_root_regions = 0; - _should_abort = false; + _claimed_root_regions.store_relaxed(0); + _should_abort.store_relaxed(false); } const MemRegion* G1CMRootMemRegions::claim_next() { - if (_should_abort) { + if (_should_abort.load_relaxed()) { // If someone has set the should_abort flag, we return null to // force the caller to bail out of their loop. return nullptr; } - if (_claimed_root_regions >= _num_root_regions) { + uint local_num_root_regions = num_root_regions(); + if (_claimed_root_regions.load_relaxed() >= local_num_root_regions) { return nullptr; } - size_t claimed_index = AtomicAccess::fetch_then_add(&_claimed_root_regions, 1u); - if (claimed_index < _num_root_regions) { + size_t claimed_index = _claimed_root_regions.fetch_then_add(1u); + if (claimed_index < local_num_root_regions) { return &_root_regions[claimed_index]; } return nullptr; } uint G1CMRootMemRegions::num_root_regions() const { - return (uint)_num_root_regions; + return (uint)_num_root_regions.load_relaxed(); } bool G1CMRootMemRegions::contains(const MemRegion mr) const { - for (uint i = 0; i < _num_root_regions; i++) { + uint local_num_root_regions = num_root_regions(); + for (uint i = 0; i < local_num_root_regions; i++) { if (_root_regions[i].equals(mr)) { return true; } @@ -437,7 +439,7 @@ bool G1CMRootMemRegions::contains(const MemRegion mr) const { void G1CMRootMemRegions::notify_scan_done() { MutexLocker x(G1RootRegionScan_lock, Mutex::_no_safepoint_check_flag); - _scan_in_progress = false; + _scan_in_progress.store_relaxed(false); G1RootRegionScan_lock->notify_all(); } @@ -448,10 +450,10 @@ void G1CMRootMemRegions::cancel_scan() { void G1CMRootMemRegions::scan_finished() { assert(scan_in_progress(), "pre-condition"); - if (!_should_abort) { - assert(_claimed_root_regions >= num_root_regions(), + if (!_should_abort.load_relaxed()) { + assert(_claimed_root_regions.load_relaxed() >= num_root_regions(), "we should have claimed all root regions, claimed %zu, length = %u", - _claimed_root_regions, num_root_regions()); + _claimed_root_regions.load_relaxed(), num_root_regions()); } notify_scan_done(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 367568aeff4..3a4cbf1b83e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -290,12 +290,12 @@ class G1CMRootMemRegions { MemRegion* _root_regions; size_t const _max_regions; - volatile size_t _num_root_regions; // Actual number of root regions. + Atomic _num_root_regions; // Actual number of root regions. - volatile size_t _claimed_root_regions; // Number of root regions currently claimed. + Atomic _claimed_root_regions; // Number of root regions currently claimed. - volatile bool _scan_in_progress; - volatile bool _should_abort; + Atomic _scan_in_progress; + Atomic _should_abort; void notify_scan_done(); @@ -312,11 +312,11 @@ public: void prepare_for_scan(); // Forces get_next() to return null so that the iteration aborts early. - void abort() { _should_abort = true; } + void abort() { _should_abort.store_relaxed(true); } // Return true if the CM thread are actively scanning root regions, // false otherwise. - bool scan_in_progress() { return _scan_in_progress; } + bool scan_in_progress() { return _scan_in_progress.load_relaxed(); } // Claim the next root MemRegion to scan atomically, or return null if // all have been claimed. From 7ccf1757859d25572d681c8e083b97ec4b6e0b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Maillard?= Date: Mon, 2 Feb 2026 10:10:21 +0000 Subject: [PATCH 078/215] 8371536: C2: VerifyIterativeGVN should assert on first detected failure Reviewed-by: epeter, mhaessig, chagedorn --- src/hotspot/share/opto/phaseX.cpp | 223 +++++++++++++++++------------- src/hotspot/share/opto/phaseX.hpp | 20 ++- 2 files changed, 141 insertions(+), 102 deletions(-) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 52badca8050..ce24c46590d 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -532,6 +532,10 @@ void PhaseValues::init_con_caches() { memset(_zcons,0,sizeof(_zcons)); } +PhaseIterGVN* PhaseValues::is_IterGVN() { + return (_phase == PhaseValuesType::iter_gvn || _phase == PhaseValuesType::ccp) ? static_cast(this) : nullptr; +} + //--------------------------------find_int_type-------------------------------- const TypeInt* PhaseValues::find_int_type(Node* n) { if (n == nullptr) return nullptr; @@ -812,7 +816,7 @@ void PhaseGVN::dump_infinite_loop_info(Node* n, const char* where) { PhaseIterGVN::PhaseIterGVN(PhaseIterGVN* igvn) : _delay_transform(igvn->_delay_transform), _worklist(*C->igvn_worklist()) { - _iterGVN = true; + _phase = PhaseValuesType::iter_gvn; assert(&_worklist == &igvn->_worklist, "sanity"); } @@ -821,7 +825,7 @@ PhaseIterGVN::PhaseIterGVN(PhaseIterGVN* igvn) : _delay_transform(igvn->_delay_t PhaseIterGVN::PhaseIterGVN() : _delay_transform(false), _worklist(*C->igvn_worklist()) { - _iterGVN = true; + _phase = PhaseValuesType::iter_gvn; uint max; // Dead nodes in the hash table inherited from GVN were not treated as @@ -1090,16 +1094,28 @@ void PhaseIterGVN::verify_optimize() { is_verify_invariants()) { ResourceMark rm; Unique_Node_List worklist; - bool failure = false; // BFS all nodes, starting at root worklist.push(C->root()); for (uint j = 0; j < worklist.size(); ++j) { Node* n = worklist.at(j); - if (is_verify_Value()) { failure |= verify_Value_for(n); } - if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, false); } - if (is_verify_Ideal()) { failure |= verify_Ideal_for(n, true); } - if (is_verify_Identity()) { failure |= verify_Identity_for(n); } - if (is_verify_invariants()) { failure |= verify_node_invariants_for(n); } + // If we get an assert here, check why the reported node was not processed again in IGVN. + // We should either make sure that this node is properly added back to the IGVN worklist + // in PhaseIterGVN::add_users_to_worklist to update it again or add an exception + // in the verification methods below if that is not possible for some reason (like Load nodes). + if (is_verify_Value()) { + verify_Value_for(n); + } + if (is_verify_Ideal()) { + verify_Ideal_for(n, false); + verify_Ideal_for(n, true); + } + if (is_verify_Identity()) { + verify_Identity_for(n); + } + if (is_verify_invariants()) { + verify_node_invariants_for(n); + } + // traverse all inputs and outputs for (uint i = 0; i < n->req(); i++) { if (n->in(i) != nullptr) { @@ -1110,11 +1126,6 @@ void PhaseIterGVN::verify_optimize() { worklist.push(n->fast_out(i)); } } - // If we get this assert, check why the reported nodes were not processed again in IGVN. - // We should either make sure that these nodes are properly added back to the IGVN worklist - // in PhaseIterGVN::add_users_to_worklist to update them again or add an exception - // in the verification code above if that is not possible for some reason (like Load nodes). - assert(!failure, "Missed optimization opportunity/broken graph in PhaseIterGVN"); } verify_empty_worklist(nullptr); @@ -1139,18 +1150,18 @@ void PhaseIterGVN::verify_empty_worklist(Node* node) { assert(false, "igvn worklist must still be empty after verify"); } -// Check that type(n) == n->Value(), return true if we have a failure. +// Check that type(n) == n->Value(), asserts if we have a failure. // We have a list of exceptions, see detailed comments in code. // (1) Integer "widen" changes, but the range is the same. // (2) LoadNode performs deep traversals. Load is not notified for changes far away. // (3) CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away. -bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { +void PhaseIterGVN::verify_Value_for(const Node* n, bool strict) { // If we assert inside type(n), because the type is still a null, then maybe // the node never went through gvn.transform, which would be a bug. const Type* told = type(n); const Type* tnew = n->Value(this); if (told == tnew) { - return false; + return; } // Exception (1) // Integer "widen" changes, but range is the same. @@ -1159,7 +1170,7 @@ bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { const TypeInteger* t1 = tnew->is_integer(tnew->basic_type()); if (t0->lo_as_long() == t1->lo_as_long() && t0->hi_as_long() == t1->hi_as_long()) { - return false; // ignore integer widen + return; // ignore integer widen } } // Exception (2) @@ -1168,7 +1179,7 @@ bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { // MemNode::can_see_stored_value looks up through many memory nodes, // which means we would need to notify modifications from far up in // the inputs all the way down to the LoadNode. We don't do that. - return false; + return; } // Exception (3) // CmpPNode performs deep traversals if it compares oopptr. CmpP is not notified for changes far away. @@ -1184,10 +1195,10 @@ bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { // control sub of the allocation. The problems is that sometimes dominates answers // false conservatively, and later it can determine that it is indeed true. Loops with // Region heads can lead to giving up, whereas LoopNodes can be skipped easier, and - // so the traversal becomes more powerful. This is difficult to remidy, we would have + // so the traversal becomes more powerful. This is difficult to remedy, we would have // to notify the CmpP of CFG updates. Luckily, we recompute CmpP::Value during CCP // after loop-opts, so that should take care of many of these cases. - return false; + return; } stringStream ss; // Print as a block without tty lock. @@ -1201,13 +1212,24 @@ bool PhaseIterGVN::verify_Value_for(Node* n, bool strict) { tnew->dump_on(&ss); ss.cr(); tty->print_cr("%s", ss.as_string()); - return true; + + switch (_phase) { + case PhaseValuesType::iter_gvn: + assert(false, "Missed Value optimization opportunity in PhaseIterGVN for %s",n->Name()); + break; + case PhaseValuesType::ccp: + assert(false, "PhaseCCP not at fixpoint: analysis result may be unsound for %s", n->Name()); + break; + default: + assert(false, "Unexpected phase"); + break; + } } // Check that all Ideal optimizations that could be done were done. -// Returns true if it found missed optimization opportunities and -// false otherwise (no missed optimization, or skipped verification). -bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { +// Asserts if it found missed optimization opportunities or encountered unexpected changes, and +// returns normally otherwise (no missed optimization, or skipped verification). +void PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // First, we check a list of exceptions, where we skip verification, // because there are known cases where Ideal can optimize after IGVN. // Some may be expected and cannot be fixed, and others should be fixed. @@ -1221,7 +1243,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xbatch --version case Op_RangeCheck: - return false; + return; // IfNode::Ideal does: // Node* prev_dom = search_identical(dist, igvn); @@ -1232,7 +1254,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_If: - return false; + return; // IfNode::simple_subsuming // Looks for dominating test that subsumes the current test. @@ -1242,7 +1264,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // runtime/exceptionMsgs/ArrayIndexOutOfBoundsException/ArrayIndexOutOfBoundsExceptionTest.java#id1 // -XX:VerifyIterativeGVN=1110 case Op_CountedLoopEnd: - return false; + return; // LongCountedLoopEndNode::Ideal // Probably same issue as above. @@ -1251,7 +1273,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/predicates/assertion/TestAssertionPredicates.java#NoLoopPredicationXbatch // -XX:StressLongCountedLoop=2000000 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_LongCountedLoopEnd: - return false; + return; // RegionNode::Ideal does "Skip around the useless IF diamond". // 245 IfTrue === 244 @@ -1270,7 +1292,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_Region: - return false; + return; // In AddNode::Ideal, we call "commute", which swaps the inputs so // that smaller idx are first. Tracking it back, it led me to @@ -1349,7 +1371,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { case Op_MulHF: case Op_MaxHF: case Op_MinHF: - return false; + return; // In MulNode::Ideal the edges can be swapped to help value numbering: // @@ -1373,7 +1395,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java // -XX:VerifyIterativeGVN=1110 case Op_AndL: - return false; + return; // SubLNode::Ideal does transform like: // Convert "c1 - (y+c0)" into "(c1-c0) - y" @@ -1405,7 +1427,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_SubL: - return false; + return; // SubINode::Ideal does // Convert "x - (y+c0)" into "(x-y) - c0" AND @@ -1417,7 +1439,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // test/hotspot/jtreg/compiler/c2/IVTest.java // -XX:VerifyIterativeGVN=1110 case Op_SubI: - return false; + return; // AddNode::IdealIL does transform like: // Convert x + (con - y) into "(x - y) + con" @@ -1446,7 +1468,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_AddL: - return false; + return; // SubTypeCheckNode::Ideal calls SubTypeCheckNode::verify_helper, which does // Node* cmp = phase->transform(new CmpPNode(subklass, in(SuperKlass))); @@ -1471,7 +1493,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xbatch --version case Op_SubTypeCheck: - return false; + return; // LoopLimitNode::Ideal when stride is constant power-of-2, we can do a lowering // to other nodes: Conv, Add, Sub, Mul, And ... @@ -1491,7 +1513,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Fond with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_LoopLimit: - return false; + return; // PhiNode::Ideal calls split_flow_path, which tries to do this: // "This optimization tries to find two or more inputs of phi with the same constant @@ -1514,7 +1536,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_Phi: - return false; + return; // MemBarNode::Ideal does "Eliminate volatile MemBars for scalar replaced objects". // For examle "The allocated object does not escape". @@ -1527,7 +1549,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_MemBarStoreStore: - return false; + return; // ConvI2LNode::Ideal converts // 648 AddI === _ 583 645 [[ 661 ]] @@ -1543,7 +1565,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version case Op_ConvI2L: - return false; + return; // AddNode::IdealIL can do this transform (and similar other ones): // Convert "a*b+a*c into a*(b+c) @@ -1557,7 +1579,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // test/hotspot/jtreg/compiler/loopopts/superword/ReductionPerf.java // -XX:VerifyIterativeGVN=1110 case Op_AddI: - return false; + return; // ArrayCopyNode::Ideal // calls ArrayCopyNode::prepare_array_copy @@ -1583,7 +1605,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/arraycopy/TestArrayCopyAsLoadsStores.java // -XX:VerifyIterativeGVN=1110 case Op_ArrayCopy: - return false; + return; // CastLLNode::Ideal // calls ConstraintCastNode::optimize_integer_cast -> pushes CastLL through SubL @@ -1595,7 +1617,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/c2/TestMergeStoresMemorySegment.java#byte-array // -XX:VerifyIterativeGVN=1110 case Op_CastLL: - return false; + return; // Similar case happens to CastII // @@ -1603,7 +1625,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/c2/TestScalarReplacementMaxLiveNodes.java // -XX:VerifyIterativeGVN=1110 case Op_CastII: - return false; + return; // MaxLNode::Ideal // calls AddNode::Ideal @@ -1618,7 +1640,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // -XX:VerifyIterativeGVN=1110 case Op_MaxL: case Op_MinL: - return false; + return; // OrINode::Ideal // calls AddNode::Ideal @@ -1632,7 +1654,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // -XX:VerifyIterativeGVN=1110 case Op_OrI: case Op_OrL: - return false; + return; // Bool -> constant folded to 1. // Issue with notification? @@ -1641,7 +1663,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/c2/irTests/TestVectorizationMismatchedAccess.java // -XX:VerifyIterativeGVN=1110 case Op_Bool: - return false; + return; // LShiftLNode::Ideal // Looks at pattern: "(x + x) << c0", converts it to "x << (c0 + 1)" @@ -1651,7 +1673,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // compiler/conversions/TestMoveConvI2LOrCastIIThruAddIs.java // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_LShiftL: - return false; + return; // LShiftINode::Ideal // pattern: ((x + con1) << con2) -> x << con2 + con1 << con2 @@ -1664,18 +1686,18 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // 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 false; + return; // AddPNode::Ideal seems to do set_req without removing lock first. // Found with various vector tests tier1-tier3. case Op_AddP: - return false; + return; // StrIndexOfNode::Ideal // Found in tier1-3. case Op_StrIndexOf: case Op_StrIndexOfChar: - return false; + return; // StrEqualsNode::Identity // @@ -1684,7 +1706,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // -XX:+UnlockExperimentalVMOptions -XX:LockingMode=1 -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 // Note: The -XX:LockingMode option is not available anymore. case Op_StrEquals: - return false; + return; // AryEqNode::Ideal // Not investigated. Reshapes itself and adds lots of nodes to the worklist. @@ -1693,22 +1715,22 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // vmTestbase/vm/mlvm/meth/stress/compiler/i2c_c2i/Test.java // -XX:+UnlockDiagnosticVMOptions -XX:-TieredCompilation -XX:+StressUnstableIfTraps -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_AryEq: - return false; + return; // MergeMemNode::Ideal // Found in tier1-3. Did not investigate further yet. case Op_MergeMem: - return false; + return; // URShiftINode::Ideal // Found in tier1-3. Did not investigate further yet. case Op_URShiftI: - return false; + return; // CMoveINode::Ideal // Found in tier1-3. Did not investigate further yet. case Op_CMoveI: - return false; + return; // CmpPNode::Ideal calls isa_const_java_mirror // and generates new constant nodes, even if no progress is made. @@ -1719,14 +1741,14 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // java -XX:VerifyIterativeGVN=1110 -Xcomp --version case Op_CmpP: - return false; + 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 false; + return; } if (n->is_Load()) { @@ -1742,7 +1764,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // test/hotspot/jtreg/compiler/arraycopy/TestCloneAccess.java // -XX:VerifyIterativeGVN=1110 - return false; + return; } if (n->is_Store()) { @@ -1756,7 +1778,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // // Found with: // java -XX:VerifyIterativeGVN=0100 -Xcomp --version - return false; + return; } if (n->is_Vector()) { @@ -1775,7 +1797,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // compiler/vectorapi/TestMaskedMacroLogicVector.java // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 -XX:+UseParallelGC -XX:+UseNUMA - return false; + return; } if (n->is_Region()) { @@ -1794,7 +1816,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // compiler/eliminateAutobox/TestShortBoxing.java // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - return false; + return; } if (n->is_CallJava()) { @@ -1826,13 +1848,19 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { // Found with: // compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-U // -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - return false; + return; } // The number of nodes shoud not increase. uint old_unique = C->unique(); // The hash of a node should not change, this would indicate different inputs uint old_hash = n->hash(); + // Remove 'n' from hash table in case it gets modified. We want to avoid + // hitting the "Need to remove from hash before changing edges" assert if + // a change occurs. Instead, we would like to proceed with the optimization, + // return and finally hit the assert in PhaseIterGVN::verify_optimize to get + // a more meaningful message + _table.hash_delete(n); Node* i = n->Ideal(this, can_reshape); // If there was no new Idealization, we are probably happy. if (i == nullptr) { @@ -1843,7 +1871,7 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { ss.print_cr(" old_unique = %d, unique = %d", old_unique, C->unique()); n->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + assert(false, "Unexpected new unused nodes from applying Ideal optimization on %s", n->Name()); } if (old_hash != n->hash()) { @@ -1853,13 +1881,14 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { ss.print_cr(" old_hash = %d, hash = %d", old_hash, n->hash()); n->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + assert(false, "Unexpected hash change from applying Ideal optimization on %s", n->Name()); } verify_empty_worklist(n); // Everything is good. - return false; + hash_find_insert(n); + return; } // We just saw a new Idealization which was not done during IGVN. @@ -1876,13 +1905,14 @@ bool PhaseIterGVN::verify_Ideal_for(Node* n, bool can_reshape) { ss.print_cr("The result after Ideal:"); i->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + + assert(false, "Missed Ideal optimization opportunity in PhaseIterGVN for %s", n->Name()); } // Check that all Identity optimizations that could be done were done. -// Returns true if it found missed optimization opportunities and -// false otherwise (no missed optimization, or skipped verification). -bool PhaseIterGVN::verify_Identity_for(Node* n) { +// Asserts if it found missed optimization opportunities, and +// returns normally otherwise (no missed optimization, or skipped verification). +void PhaseIterGVN::verify_Identity_for(Node* n) { // First, we check a list of exceptions, where we skip verification, // because there are known cases where Ideal can optimize after IGVN. // Some may be expected and cannot be fixed, and others should be fixed. @@ -1901,7 +1931,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // Found with: // java -XX:VerifyIterativeGVN=1000 -Xcomp --version case Op_SafePoint: - return false; + return; // MergeMemNode::Identity replaces the MergeMem with its base_memory if it // does not record any other memory splits. @@ -1912,7 +1942,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // Found with: // java -XX:VerifyIterativeGVN=1000 -Xcomp --version case Op_MergeMem: - return false; + return; // ConstraintCastNode::Identity finds casts that are the same, except that // the control is "higher up", i.e. dominates. The call goes via @@ -1926,7 +1956,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { case Op_CastPP: case Op_CastII: case Op_CastLL: - return false; + return; // Same issue for CheckCastPP, uses ConstraintCastNode::Identity and // checks dominator, which may be changed, but too far up for notification @@ -1936,7 +1966,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // compiler/c2/irTests/TestSkeletonPredicates.java // -XX:VerifyIterativeGVN=1110 case Op_CheckCastPP: - return false; + return; // In SubNode::Identity, we do: // Convert "(X+Y) - Y" into X and "(X+Y) - X" into Y @@ -1954,7 +1984,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // java -XX:VerifyIterativeGVN=1000 -Xcomp --version case Op_SubI: case Op_SubL: - return false; + return; // PhiNode::Identity checks for patterns like: // r = (x != con) ? x : con; @@ -1968,7 +1998,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithG1.java // -XX:VerifyIterativeGVN=1110 case Op_Phi: - return false; + return; // ConvI2LNode::Identity does // convert I2L(L2I(x)) => x @@ -1979,7 +2009,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // compiler/loopopts/superword/TestDependencyOffsets.java#vanilla-A // -XX:VerifyIterativeGVN=1110 case Op_ConvI2L: - return false; + return; // MaxNode::find_identity_operation // Finds patterns like Max(A, Max(A, B)) -> Max(A, B) @@ -1999,7 +2029,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { case Op_MinHF: case Op_MaxD: case Op_MinD: - return false; + return; // AddINode::Identity @@ -2013,12 +2043,12 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 case Op_AddI: case Op_AddL: - return false; + return; // AbsINode::Identity // Not investigated yet. case Op_AbsI: - return false; + return; } if (n->is_Load()) { @@ -2032,7 +2062,7 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // // Found with: // java -XX:VerifyIterativeGVN=1000 -Xcomp --version - return false; + return; } if (n->is_Store()) { @@ -2043,20 +2073,20 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { // Found with: // applications/ctw/modules/java_base_2.java // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -Djava.awt.headless=true -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - return false; + return; } if (n->is_Vector()) { // Found with tier1-3. Not investigated yet. // The observed issue was with AndVNode::Identity - return false; + return; } Node* i = n->Identity(this); // If we cannot find any other Identity, we are happy. if (i == n) { verify_empty_worklist(n); - return false; + return; } // The verification just found a new Identity that was not found during IGVN. @@ -2068,11 +2098,12 @@ bool PhaseIterGVN::verify_Identity_for(Node* n) { ss.print_cr("New node:"); i->dump_bfs(1, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + + assert(false, "Missed Identity optimization opportunity in PhaseIterGVN for %s", n->Name()); } // Some other verifications that are not specific to a particular transformation. -bool PhaseIterGVN::verify_node_invariants_for(const Node* n) { +void PhaseIterGVN::verify_node_invariants_for(const Node* n) { if (n->is_AddP()) { if (!n->as_AddP()->address_input_has_same_base()) { stringStream ss; // Print as a block without tty lock. @@ -2080,10 +2111,10 @@ bool PhaseIterGVN::verify_node_invariants_for(const Node* n) { ss.print_cr("Base pointers must match for AddP chain:"); n->dump_bfs(2, nullptr, "", &ss); tty->print_cr("%s", ss.as_string()); - return true; + + assert(false, "Broken node invariant for %s", n->Name()); } } - return false; } #endif @@ -2794,6 +2825,7 @@ uint PhaseCCP::_total_constants = 0; PhaseCCP::PhaseCCP( PhaseIterGVN *igvn ) : PhaseIterGVN(igvn) { NOT_PRODUCT( clear_constants(); ) assert( _worklist.size() == 0, "" ); + _phase = PhaseValuesType::ccp; analyze(); } @@ -2914,17 +2946,18 @@ bool PhaseCCP::needs_revisit(Node* n) const { // Note for CCP the non-convergence can lead to unsound analysis and mis-compilation. // Therefore, we are verifying Value convergence strictly. void PhaseCCP::verify_analyze(Unique_Node_List& worklist_verify) { - bool failure = false; while (worklist_verify.size()) { Node* n = worklist_verify.pop(); - failure |= verify_Value_for(n, /* strict = */ true); + + // An assert in verify_Value_for means that PhaseCCP is not at fixpoint + // and that the analysis result may be unsound. + // If this happens, check why the reported nodes were not processed again in CCP. + // We should either make sure that these nodes are properly added back to the CCP worklist + // in PhaseCCP::push_child_nodes_to_worklist() to update their type in the same round, + // or that they are added in PhaseCCP::needs_revisit() so that analysis revisits + // them at the end of the round. + verify_Value_for(n, true); } - // If we get this assert, check why the reported nodes were not processed again in CCP. - // We should either make sure that these nodes are properly added back to the CCP worklist - // in PhaseCCP::push_child_nodes_to_worklist() to update their type in the same round, - // or that they are added in PhaseCCP::needs_revisit() so that analysis revisits - // them at the end of the round. - assert(!failure, "PhaseCCP not at fixpoint: analysis result may be unsound."); } #endif diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 3f75aab8980..ce02f456c00 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -224,7 +224,13 @@ public: // 3) NodeHash table, to find identical nodes (and remove/update the hash of a node on modification). class PhaseValues : public PhaseTransform { protected: - bool _iterGVN; + enum class PhaseValuesType { + gvn, + iter_gvn, + ccp + }; + + PhaseValuesType _phase; // Hash table for value-numbering. Reference to "C->node_hash()", NodeHash &_table; @@ -247,7 +253,7 @@ protected: void init_con_caches(); public: - PhaseValues() : PhaseTransform(GVN), _iterGVN(false), + PhaseValues() : PhaseTransform(GVN), _phase(PhaseValuesType::gvn), _table(*C->node_hash()), _types(*C->types()) { NOT_PRODUCT( clear_new_values(); ) @@ -256,7 +262,7 @@ public: init_con_caches(); } NOT_PRODUCT(~PhaseValues();) - PhaseIterGVN* is_IterGVN() { return (_iterGVN) ? (PhaseIterGVN*)this : nullptr; } + PhaseIterGVN* is_IterGVN(); // Some Ideal and other transforms delete --> modify --> insert values bool hash_delete(Node* n) { return _table.hash_delete(n); } @@ -490,10 +496,10 @@ public: void optimize(); #ifdef ASSERT void verify_optimize(); - bool verify_Value_for(Node* n, bool strict = false); - bool verify_Ideal_for(Node* n, bool can_reshape); - bool verify_Identity_for(Node* n); - bool verify_node_invariants_for(const Node* n); + void verify_Value_for(const Node* n, bool strict = false); + void verify_Ideal_for(Node* n, bool can_reshape); + void verify_Identity_for(Node* n); + void verify_node_invariants_for(const Node* n); void verify_empty_worklist(Node* n); #endif From 90a43f8445de4e66da6ae113c2b4d40ee88c4a73 Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Mon, 2 Feb 2026 10:11:34 +0000 Subject: [PATCH 079/215] 8376325: [IR Framework] Detect and report overloads Reviewed-by: chagedorn, dfenacci --- .../lib/ir_framework/test/TestVM.java | 5 + .../ir_framework/tests/TestBadFormat.java | 5 +- .../ir_framework/tests/TestBasics.java | 272 ++++++++---------- .../ir_framework/tests/TestControls.java | 91 ++---- 4 files changed, 153 insertions(+), 220 deletions(-) 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 c2580e087f0..14551141cd7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java @@ -588,6 +588,11 @@ public class TestVM { } private void checkTestAnnotations(Method m, Test testAnno) { + List overloads = Arrays.stream(testClass.getDeclaredMethods()).filter(other -> !m.equals(other) && m.getName().equals(other.getName())).toList(); + TestFormat.check(overloads.isEmpty(), + "Cannot overload @Test methods, but method " + m + " has " + overloads.size() + " overload" + (overloads.size() == 1 ? "" : "s") + ":" + + overloads.stream().map(String::valueOf).collect(Collectors.joining("\n - ", "\n - ", "")) + ); TestFormat.check(!testMethodMap.containsKey(m.getName()), "Cannot overload two @Test methods: " + m + ", " + testMethodMap.get(m.getName())); TestFormat.check(testAnno != null, m + " must be a method with a @Test annotation"); diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBadFormat.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBadFormat.java index 200866375af..da8fd6489b8 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBadFormat.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBadFormat.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 @@ -276,9 +276,10 @@ class BadArgumentsAnnotation { } } +// Since all the methods are failing, the class doesn't specify any @Test methods, which is another failure. +@ClassFail class BadOverloadedMethod { - @FailCount(0) // Combined with both sameName() below @Test public void sameName() {} diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBasics.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBasics.java index b5006290047..8efdcb4f021 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBasics.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestBasics.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 @@ -28,6 +28,8 @@ import compiler.lib.ir_framework.test.TestVM; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.stream.Stream; /* @@ -44,7 +46,7 @@ import java.util.stream.Stream; public class TestBasics { private static boolean wasExecuted = false; private boolean lastToggleBoolean = true; - private final static int[] executed = new int[100]; + private final static HashMap executed = HashMap.newHashMap(100); private final static int[] executedOnce = new int[5]; private long[] nonFloatingRandomNumbers = new long[10]; private double[] floatingRandomNumbers = new double[10]; @@ -60,12 +62,12 @@ public class TestBasics { if (wasExecuted) { throw new RuntimeException("Executed non @Test method or a method that was not intended to be run"); } - for (int i = 0; i < executed.length; i++) { - int value = executed[i]; + for (Map.Entry entry : executed.entrySet()) { + int value = entry.getValue(); if (value != TestVM.WARMUP_ITERATIONS + 1) { // Warmups + 1 C2 compiled invocation - throw new RuntimeException("Test " + i + " was executed " + value + " times instead stead of " - + (TestVM.WARMUP_ITERATIONS + 1) + " times." ); + throw new RuntimeException("Test " + entry.getKey() + " was executed " + value + " times instead stead of " + + (TestVM.WARMUP_ITERATIONS + 1) + " times." ); } } @@ -88,12 +90,6 @@ public class TestBasics { randomBooleans = new Boolean[64]; } - // Base test, no arguments, directly invoked. - @Test - public void test() { - executed[0]++; - } - // Not a test public void noTest() { wasExecuted = true; @@ -109,24 +105,19 @@ public class TestBasics { wasExecuted = true; } - // Can overload a @Test if it is not a @Test itself. - public static void test(double i) { - wasExecuted = true; - } - @Test public static void staticTest() { - executed[1]++; + executed.merge("staticTest", 1, Integer::sum); } @Test public final void finalTest() { - executed[2]++; + executed.merge("finalTest", 1, Integer::sum); } @Test public int returnValueTest() { - executed[3]++; + executed.merge("returnValueTest", 1, Integer::sum); return 4; } @@ -135,7 +126,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void byteDefaultArgument(byte x) { - executed[4]++; + executed.merge("byteDefaultArgument", 1, Integer::sum); if (x != 0) { throw new RuntimeException("Must be 0"); } @@ -144,7 +135,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void shortDefaultArgument(short x) { - executed[5]++; + executed.merge("shortDefaultArgument", 1, Integer::sum); if (x != 0) { throw new RuntimeException("Must be 0"); } @@ -153,7 +144,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void intDefaultArgument(int x) { - executed[6]++; + executed.merge("intDefaultArgument", 1, Integer::sum); if (x != 0) { throw new RuntimeException("Must be 0"); } @@ -162,7 +153,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void longDefaultArgument(long x) { - executed[7]++; + executed.merge("longDefaultArgument", 1, Integer::sum); if (x != 0L) { throw new RuntimeException("Must be 0"); } @@ -171,7 +162,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void floatDefaultArgument(float x) { - executed[8]++; + executed.merge("floatDefaultArgument", 1, Integer::sum); if (x != 0.0f) { throw new RuntimeException("Must be 0.0"); } @@ -180,7 +171,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void doubleDefaultArgument(double x) { - executed[9]++; + executed.merge("doubleDefaultArgument", 1, Integer::sum); if (x != 0.0f) { throw new RuntimeException("Must be 0.0"); } @@ -189,7 +180,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void charDefaultArgument(char x) { - executed[10]++; + executed.merge("charDefaultArgument", 1, Integer::sum); if (x != '\u0000') { throw new RuntimeException("Must be \u0000"); } @@ -198,7 +189,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void booleanDefaultArgument(boolean x) { - executed[11]++; + executed.merge("booleanDefaultArgument", 1, Integer::sum); if (x) { throw new RuntimeException("Must be false"); } @@ -207,7 +198,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void stringObjectDefaultArgument(String x) { - executed[12]++; + executed.merge("stringObjectDefaultArgument", 1, Integer::sum); if (x == null || x.length() != 0) { throw new RuntimeException("Default string object must be non-null and having a length of zero"); } @@ -216,7 +207,7 @@ public class TestBasics { @Test @Arguments(values = Argument.DEFAULT) public void defaultObjectDefaultArgument(DefaultObject x) { - executed[13]++; + executed.merge("defaultObjectDefaultArgument", 1, Integer::sum); if (x == null || x.i != 4) { throw new RuntimeException("Default object must not be null and its i field must be 4"); } @@ -225,7 +216,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void byte42(byte x) { - executed[14]++; + executed.merge("byte42", 1, Integer::sum); if (x != 42) { throw new RuntimeException("Must be 42"); } @@ -234,7 +225,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void short42(short x) { - executed[15]++; + executed.merge("short42", 1, Integer::sum); if (x != 42) { throw new RuntimeException("Must be 42"); } @@ -243,7 +234,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void int42(int x) { - executed[16]++; + executed.merge("int42", 1, Integer::sum); if (x != 42) { throw new RuntimeException("Must be 42"); } @@ -252,7 +243,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void long42(long x) { - executed[17]++; + executed.merge("long42", 1, Integer::sum); if (x != 42) { throw new RuntimeException("Must be 42"); } @@ -261,7 +252,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void float42(float x) { - executed[18]++; + executed.merge("float42", 1, Integer::sum); if (x != 42.0) { throw new RuntimeException("Must be 42"); } @@ -270,7 +261,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_42) public void double42(double x) { - executed[19]++; + executed.merge("double42", 1, Integer::sum); if (x != 42.0) { throw new RuntimeException("Must be 42"); } @@ -279,7 +270,7 @@ public class TestBasics { @Test @Arguments(values = Argument.FALSE) public void booleanFalse(boolean x) { - executed[20]++; + executed.merge("booleanFalse", 1, Integer::sum); if (x) { throw new RuntimeException("Must be false"); } @@ -288,7 +279,7 @@ public class TestBasics { @Test @Arguments(values = Argument.TRUE) public void booleanTrue(boolean x) { - executed[21]++; + executed.merge("booleanTrue", 1, Integer::sum); if (!x) { throw new RuntimeException("Must be true"); } @@ -297,37 +288,37 @@ public class TestBasics { @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomByte(byte x) { - executed[22]++; + executed.merge("randomByte", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomShort(short x) { - executed[23]++; + executed.merge("randomShort", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomInt(int x) { - executed[24]++; + executed.merge("randomInt", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomLong(long x) { - executed[25]++; + executed.merge("randomLong", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomFloat(float x) { - executed[26]++; + executed.merge("randomFloat", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomDouble(double x) { - executed[27]++; + executed.merge("randomDouble", 1, Integer::sum); } // Not executed @@ -338,13 +329,13 @@ public class TestBasics { @Test @Arguments(values = Argument.RANDOM_ONCE) public void randomBoolean(boolean x) { - executed[28]++; + executed.merge("randomBoolean", 1, Integer::sum); } @Test @Arguments(values = Argument.BOOLEAN_TOGGLE_FIRST_FALSE) public void booleanToggleFirstFalse(boolean x) { - if (executed[29] == 0) { + if (!executed.containsKey("booleanToggleFirstFalse")) { // First invocation if (x) { throw new RuntimeException("BOOLEAN_TOGGLE_FIRST_FALSE must be false on first invocation"); @@ -353,63 +344,63 @@ public class TestBasics { throw new RuntimeException("BOOLEAN_TOGGLE_FIRST_FALSE did not toggle"); } lastToggleBoolean = x; - executed[29]++; + executed.merge("booleanToggleFirstFalse", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachByte(byte x) { - checkNonFloatingRandomNumber(x, executed[30]); - executed[30]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachByte", 0)); + executed.merge("randomEachByte", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachShort(short x) { - checkNonFloatingRandomNumber(x, executed[31]); - executed[31]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachShort", 0)); + executed.merge("randomEachShort", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachInt(int x) { - checkNonFloatingRandomNumber(x, executed[32]); - executed[32]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachInt", 0)); + executed.merge("randomEachInt", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachLong(long x) { - checkNonFloatingRandomNumber(x, executed[33]); - executed[33]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachLong", 0)); + executed.merge("randomEachLong", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachChar(char x) { - checkNonFloatingRandomNumber(x, executed[34]); - executed[34]++; + checkNonFloatingRandomNumber(x, executed.getOrDefault("randomEachChar", 0)); + executed.merge("randomEachChar", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachFloat(float x) { - checkFloatingRandomNumber(x, executed[35]); - executed[35]++; + checkFloatingRandomNumber(x, executed.getOrDefault("randomEachFloat", 0)); + executed.merge("randomEachFloat", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachDouble(double x) { - checkFloatingRandomNumber(x, executed[36]); - executed[36]++; + checkFloatingRandomNumber(x, executed.getOrDefault("randomEachDouble", 0)); + executed.merge("randomEachDouble", 1, Integer::sum); } @Test @Arguments(values = Argument.RANDOM_EACH) public void randomEachBoolean(boolean x) { - checkRandomBoolean(x, executed[37]); - executed[37]++; + checkRandomBoolean(x, executed.getOrDefault("randomEachBoolean", 0)); + executed.merge("randomEachBoolean", 1, Integer::sum); } private void checkNonFloatingRandomNumber(long x, int invocationCount) { @@ -461,7 +452,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void byteMinus42(byte x) { - executed[38]++; + executed.merge("byteMinus42", 1, Integer::sum); if (x != -42) { throw new RuntimeException("Must be -42"); } @@ -470,7 +461,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void shortMinus42(short x) { - executed[39]++; + executed.merge("shortMinus42", 1, Integer::sum); if (x != -42) { throw new RuntimeException("Must be -42"); } @@ -479,7 +470,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void intMinus42(int x) { - executed[40]++; + executed.merge("intMinus42", 1, Integer::sum); if (x != -42) { throw new RuntimeException("Must be -42"); } @@ -488,7 +479,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void longMinus42(long x) { - executed[41]++; + executed.merge("longMinus42", 1, Integer::sum); if (x != -42) { throw new RuntimeException("Must be -42"); } @@ -497,7 +488,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void floatMinus42(float x) { - executed[42]++; + executed.merge("floatMinus42", 1, Integer::sum); if (x != -42.0) { throw new RuntimeException("Must be -42"); } @@ -506,7 +497,7 @@ public class TestBasics { @Test @Arguments(values = Argument.NUMBER_MINUS_42) public void doubleMinus42(double x) { - executed[43]++; + executed.merge("doubleMinus42", 1, Integer::sum); if (x != -42.0) { throw new RuntimeException("Must be -42"); } @@ -515,7 +506,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void byteMin(byte x) { - executed[79]++; + executed.merge("byteMin", 1, Integer::sum); if (x != Byte.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -524,7 +515,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void charMin(char x) { - executed[80]++; + executed.merge("charMin", 1, Integer::sum); if (x != Character.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -533,7 +524,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void shortMin(short x) { - executed[81]++; + executed.merge("shortMin", 1, Integer::sum); if (x != Short.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -542,7 +533,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void intMin(int x) { - executed[82]++; + executed.merge("intMin", 1, Integer::sum); if (x != Integer.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -551,7 +542,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void longMin(long x) { - executed[83]++; + executed.merge("longMin", 1, Integer::sum); if (x != Long.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -560,7 +551,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void floatMin(float x) { - executed[84]++; + executed.merge("floatMin", 1, Integer::sum); if (x != Float.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -569,7 +560,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MIN) public void doubleMin(double x) { - executed[85]++; + executed.merge("doubleMin", 1, Integer::sum); if (x != Double.MIN_VALUE) { throw new RuntimeException("Must be MIN_VALUE"); } @@ -578,7 +569,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void byteMax(byte x) { - executed[86]++; + executed.merge("byteMax", 1, Integer::sum); if (x != Byte.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -587,7 +578,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void charMax(char x) { - executed[87]++; + executed.merge("charMax", 1, Integer::sum); if (x != Character.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -596,7 +587,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void shortMax(short x) { - executed[88]++; + executed.merge("shortMax", 1, Integer::sum); if (x != Short.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -605,7 +596,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void intMax(int x) { - executed[89]++; + executed.merge("intMax", 1, Integer::sum); if (x != Integer.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -614,7 +605,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void longMax(long x) { - executed[90]++; + executed.merge("longMax", 1, Integer::sum); if (x != Long.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -623,7 +614,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void floatMax(float x) { - executed[91]++; + executed.merge("floatMax", 1, Integer::sum); if (x != Float.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -632,7 +623,7 @@ public class TestBasics { @Test @Arguments(values = Argument.MAX) public void doubleMax(double x) { - executed[78]++; + executed.merge("doubleMax", 1, Integer::sum); if (x != Double.MAX_VALUE) { throw new RuntimeException("Must be MAX_VALUE"); } @@ -641,7 +632,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault1(byte x, short y) { - executed[44]++; + executed.merge("twoArgsDefault1", 1, Integer::sum); if (x != 0 || y != 0) { throw new RuntimeException("Both must be 0"); } @@ -650,7 +641,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault2(int x, short y) { - executed[45]++; + executed.merge("twoArgsDefault2", 1, Integer::sum); if (x != 0 || y != 0) { throw new RuntimeException("Both must be 0"); } @@ -659,7 +650,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault3(short x, long y) { - executed[46]++; + executed.merge("twoArgsDefault3", 1, Integer::sum); if (x != 0 || y != 0) { throw new RuntimeException("Both must be 0"); } @@ -668,7 +659,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault4(float x, boolean y) { - executed[47]++; + executed.merge("twoArgsDefault4", 1, Integer::sum); if (x != 0.0 || y) { throw new RuntimeException("Must be 0 and false"); } @@ -677,7 +668,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault5(boolean x, char y) { - executed[48]++; + executed.merge("twoArgsDefault5", 1, Integer::sum); if (x || y != '\u0000') { throw new RuntimeException("Must be false and \u0000"); } @@ -686,7 +677,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) public void twoArgsDefault6(char x, byte y) { - executed[49]++; + executed.merge("twoArgsDefault6", 1, Integer::sum); if (x != '\u0000' || y != 0) { throw new RuntimeException("Must be\u0000 and 0"); } @@ -695,7 +686,7 @@ public class TestBasics { @Test @Arguments(values = {Argument.RANDOM_ONCE, Argument.RANDOM_ONCE}) public void twoArgsRandomOnce(char x, byte y) { - executed[50]++; + executed.merge("twoArgsRandomOnce", 1, Integer::sum); } @Test @@ -707,7 +698,7 @@ public class TestBasics { if (Stream.of(a, b, c, d, e, f, g, h).allMatch(i -> i == a)) { throw new RuntimeException("RANDOM_ONCE does not produce random values for different arguments"); } - executed[51]++; + executed.merge("checkRandomOnceDifferentArgs", 1, Integer::sum); } @Test @@ -716,7 +707,7 @@ public class TestBasics { Argument.RANDOM_ONCE, Argument.RANDOM_ONCE, Argument.RANDOM_ONCE, Argument.RANDOM_ONCE}) public void checkMixedRandoms1(byte a, short b, int c, long d, char e, boolean f, float g, double h) { - executed[52]++; + executed.merge("checkMixedRandoms1", 1, Integer::sum); } @Test @@ -725,7 +716,7 @@ public class TestBasics { Argument.RANDOM_EACH, Argument.RANDOM_EACH, Argument.RANDOM_EACH, Argument.RANDOM_EACH}) public void checkMixedRandoms2(byte a, short b, int c, long d, char e, boolean f, float g, double h) { - executed[53]++; + executed.merge("checkMixedRandoms2", 1, Integer::sum); } @Test @@ -734,7 +725,7 @@ public class TestBasics { Argument.RANDOM_ONCE, Argument.RANDOM_EACH, Argument.RANDOM_EACH, Argument.RANDOM_ONCE}) public void checkMixedRandoms3(byte a, short b, int c, long d, char e, boolean f, float g, double h) { - executed[54]++; + executed.merge("checkMixedRandoms3", 1, Integer::sum); } @Test @@ -745,7 +736,7 @@ public class TestBasics { if (a != 42 || b != 42 || c != 42 || d != 42 || e != 42.0 || f != 42.0) { throw new RuntimeException("Must all be 42"); } - executed[55]++; + executed.merge("check42Mix1", 1, Integer::sum); } @Test @@ -756,7 +747,7 @@ public class TestBasics { if (a != -42 || b != -42 || c != -42 || d != -42 || e != -42.0 || f != -42.0) { throw new RuntimeException("Must all be -42"); } - executed[56]++; + executed.merge("check42Mix2", 1, Integer::sum); } @Test @@ -767,14 +758,14 @@ public class TestBasics { if (a != -42 || b != 42 || c != -42 || d != -42 || e != 42.0 || f != -42.0) { throw new RuntimeException("Do not match the right 42 version"); } - executed[57]++; + executed.merge("check42Mix3", 1, Integer::sum); } @Test @Arguments(values = Argument.BOOLEAN_TOGGLE_FIRST_TRUE) public void booleanToggleFirstTrue(boolean x) { - if (executed[58] == 0) { + if (executed.getOrDefault("booleanToggleFirstTrue", 0) == 0) { // First invocation if (!x) { throw new RuntimeException("BOOLEAN_TOGGLE_FIRST_FALSE must be false on first invocation"); @@ -783,13 +774,13 @@ public class TestBasics { throw new RuntimeException("BOOLEAN_TOGGLE_FIRST_FALSE did not toggle"); } lastToggleBoolean = x; - executed[58]++; + executed.merge("booleanToggleFirstTrue", 1, Integer::sum); } @Test @Arguments(values = {Argument.BOOLEAN_TOGGLE_FIRST_FALSE, Argument.BOOLEAN_TOGGLE_FIRST_TRUE}) public void checkTwoToggles(boolean b1, boolean b2) { - if (executed[59] == 0) { + if (executed.getOrDefault("checkTwoToggles", 0) == 0) { // First invocation if (b1 || !b2) { throw new RuntimeException("BOOLEAN_TOGGLES have wrong initial value"); @@ -800,14 +791,14 @@ public class TestBasics { throw new RuntimeException("Booleans did not toggle"); } lastToggleBoolean = b1; - executed[59]++; + executed.merge("checkTwoToggles", 1, Integer::sum); } @Test @Arguments(values = {Argument.BOOLEAN_TOGGLE_FIRST_FALSE, Argument.FALSE, Argument.TRUE, Argument.BOOLEAN_TOGGLE_FIRST_TRUE}) public void booleanMix(boolean b1, boolean b2, boolean b3, boolean b4) { - if (executed[60] == 0) { + if (executed.getOrDefault("booleanMix", 0) == 0) { // First invocation if (b1 || b2 || !b3 || !b4) { throw new RuntimeException("BOOLEAN_TOGGLES have wrong initial value"); @@ -818,7 +809,7 @@ public class TestBasics { throw new RuntimeException("Booleans did not toggle"); } lastToggleBoolean = b1; - executed[60]++; + executed.merge("booleanMix", 1, Integer::sum); } /* @@ -827,19 +818,19 @@ public class TestBasics { @Test public int testCheck() { - executed[63]++; + executed.merge("testCheck", 1, Integer::sum); return 1; } // Checked test. Check invoked after invoking "testCheck". Perform some more things after invocation. @Check(test = "testCheck") public void checkTestCheck() { - executed[64]++; // Executed on each invocation + executed.merge("checkTestCheck", 1, Integer::sum); // Executed on each invocation } @Test public int testCheckReturn() { - executed[65]++; + executed.merge("testCheckReturn", 1, Integer::sum); return 2; } @@ -849,13 +840,13 @@ public class TestBasics { if (returnValue != 2) { throw new RuntimeException("Must be 2"); } - executed[66]++; // Executed on each invocation + executed.merge("checkTestCheckReturn", 1, Integer::sum); // Executed on each invocation } @Test @Arguments(values = Argument.NUMBER_42) public short testCheckWithArgs(short x) { - executed[94]++; + executed.merge("testCheckWithArgs", 1, Integer::sum); return x; } @@ -864,25 +855,25 @@ public class TestBasics { if (returnValue != 42) { throw new RuntimeException("Must be 42"); } - executed[95]++; // Executed on each invocation + executed.merge("checkTestCheckWithArgs", 1, Integer::sum); // Executed on each invocation } @Test public int testCheckTestInfo() { - executed[67]++; + executed.merge("testCheckTestInfo", 1, Integer::sum); return 3; } // Checked test with info object about test. @Check(test = "testCheckTestInfo") public void checkTestCheckTestInfo(TestInfo testInfo) { - executed[68]++; // Executed on each invocation + executed.merge("checkTestCheckTestInfo(TestInfo)", 1, Integer::sum); // Executed on each invocation } @Test public int testCheckBoth() { - executed[69]++; + executed.merge("testCheckBoth", 1, Integer::sum); return 4; } @@ -892,12 +883,12 @@ public class TestBasics { if (returnValue != 4) { throw new RuntimeException("Must be 4"); } - executed[70]++; // Executed on each invocation + executed.merge("checkTestCheckTestInfo(int, TestInfo)", 1, Integer::sum); // Executed on each invocation } @Test public int testCheckOnce() { - executed[71]++; + executed.merge("testCheckOnce", 1, Integer::sum); return 1; } @@ -909,7 +900,7 @@ public class TestBasics { @Test public int testCheckReturnOnce() { - executed[72]++; + executed.merge("testCheckReturnOnce", 1, Integer::sum); return 2; } @@ -923,7 +914,7 @@ public class TestBasics { @Test public int testCheckTestInfoOnce() { - executed[73]++; + executed.merge("testCheckTestInfoOnce", 1, Integer::sum); return 3; } @@ -934,7 +925,7 @@ public class TestBasics { @Test public int testCheckBothOnce() { - executed[74]++; + executed.merge("testCheckBothOnce", 1, Integer::sum); return 4; } @@ -946,42 +937,9 @@ public class TestBasics { executedOnce[3]++; // Executed once } - @Test - public void sameName() { - executed[76]++; - } - - // Allowed to overload test method if not test method itself - public void sameName(boolean a) { - wasExecuted = true; - } - - // Allowed to overload test method if not test method itself - @Check(test = "sameName") - public void sameName(TestInfo info) { - executed[77]++; - } - - - /* - * Custom run tests. - */ - - @Test - public void sameName2() { - executed[92]++; - } - - // Allowed to overload test method if not test method itself - @Run(test = "sameName2") - public void sameName2(RunInfo info) { - executed[93]++; - sameName2(); - } - @Test public void testRun() { - executed[61]++; + executed.merge("testRun", 1, Integer::sum); } // Custom run test. This method is invoked each time instead of @Test method. This method responsible for calling @@ -993,7 +951,7 @@ public class TestBasics { @Test public void testRunNoTestInfo(int i) { // Argument allowed when run by @Run - executed[62]++; + executed.merge("testRunNoTestInfo", 1, Integer::sum); } @Run(test = "testRunNoTestInfo") @@ -1025,7 +983,7 @@ public class TestBasics { @Test public void testRunOnce2() { - executed[75]++; + executed.merge("testRunOnce2", 1, Integer::sum); } @Run(test = "testRunOnce2", mode = RunMode.STANDALONE) @@ -1037,12 +995,12 @@ public class TestBasics { @Test public void testRunMultiple() { - executed[96]++; + executed.merge("testRunMultiple", 1, Integer::sum); } @Test public void testRunMultiple2() { - executed[97]++; + executed.merge("testRunMultiple2", 1, Integer::sum); } @Test @@ -1059,12 +1017,12 @@ public class TestBasics { @Test public void testRunMultiple3() { - executed[98]++; + executed.merge("testRunMultiple3", 1, Integer::sum); } @Test public void testRunMultiple4() { - executed[99]++; + executed.merge("testRunMultiple4", 1, Integer::sum); } @Test diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestControls.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestControls.java index 7fa834b3e55..05b7007c9bd 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestControls.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestControls.java @@ -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 @@ -30,6 +30,7 @@ import jdk.test.lib.Asserts; import jdk.test.whitebox.WhiteBox; import java.lang.reflect.Method; +import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -46,7 +47,7 @@ import java.util.regex.Pattern; */ public class TestControls { - static int[] executed = new int[15]; + static HashMap executed = HashMap.newHashMap(15); static boolean wasExecuted = false; static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @@ -56,31 +57,21 @@ public class TestControls { Method runTestsOnSameVM = TestVM.class.getDeclaredMethod("runTestsOnSameVM", Class.class); runTestsOnSameVM.setAccessible(true); runTestsOnSameVM.invoke(null, new Object[]{ null }); - final int defaultIterations = TestVM.WARMUP_ITERATIONS + 1; - Asserts.assertEQ(executed[0], 1001); - Asserts.assertEQ(executed[1], 101); - Asserts.assertEQ(executed[2], 10000); - Asserts.assertEQ(executed[3], 10000); - Asserts.assertEQ(executed[4], defaultIterations); - Asserts.assertEQ(executed[5], defaultIterations); - Asserts.assertEQ(executed[6], 5001); - Asserts.assertEQ(executed[7], 5001); - Asserts.assertEQ(executed[8], 1); - Asserts.assertEQ(executed[9], 5000); - Asserts.assertEQ(executed[10], 1); - Asserts.assertEQ(executed[11], 2); - Asserts.assertEQ(executed[12], 1); - Asserts.assertEQ(executed[13], 1); + Asserts.assertEQ(executed.get("test1"), 1001); + Asserts.assertEQ(executed.get("test2"), 101); + Asserts.assertEQ(executed.get("testDontCompile"), 10000); + Asserts.assertEQ(executed.get("dontCompile"), 10000); + Asserts.assertEQ(executed.get("testCompileAtLevel1"), 5001); + Asserts.assertEQ(executed.get("dontCompile2"), 5001); + Asserts.assertEQ(executed.get("runTestDontCompile2A"), 1); + Asserts.assertEQ(executed.get("runTestDontCompile2B"), 5000); + Asserts.assertEQ(executed.get("noWarmup"), 1); + Asserts.assertEQ(executed.get("noWarmup2"), 2); + Asserts.assertEQ(executed.get("runNoWarmup2"), 1); + Asserts.assertEQ(executed.get("runTestCompilation"), 1); Asserts.assertFalse(wasExecuted); final long started = System.currentTimeMillis(); long elapsed = 0; - Method overloadDouble = TestControls.class.getDeclaredMethod("overload", double.class); - Method overloadInt = TestControls.class.getDeclaredMethod("overload", int.class); - while (!(TestFramework.isC2Compiled(overloadInt) && TestFramework.isCompiledAtLevel(overloadDouble, CompLevel.C1_LIMITED_PROFILE)) && elapsed < 5000) { - elapsed = System.currentTimeMillis() - started; - } - TestFramework.assertCompiledAtLevel(TestControls.class.getDeclaredMethod("overload", double.class), CompLevel.C1_LIMITED_PROFILE); - TestFramework.assertCompiledByC2(TestControls.class.getDeclaredMethod("overload", int.class)); TestFramework framework = new TestFramework(ClassInitializerTest.class); framework.addFlags("-XX:+PrintCompilation").addHelperClasses(ClassInitializerHelper.class).start(); @@ -99,15 +90,15 @@ public class TestControls { @Test @Warmup(1000) public void test1() { - executed[0]++; + executed.merge("test1", 1, Integer::sum); } @Check(test = "test1") public void check1(TestInfo info) { - if (executed[0] <= 1000) { + if (executed.getOrDefault("test1", 0) <= 1000) { Asserts.assertTrue(info.isWarmUp()); } else { - Asserts.assertTrue(!info.isWarmUp() && executed[0] == 1001); + Asserts.assertTrue(!info.isWarmUp() && executed.getOrDefault("test1", 0) == 1001); TestFramework.assertCompiledByC2(info.getTest()); } } @@ -115,45 +106,23 @@ public class TestControls { @Test @Warmup(100) public void test2() { - executed[1]++; + executed.merge("test2", 1, Integer::sum); } @Check(test = "test2", when = CheckAt.COMPILED) public void check2(TestInfo info) { - Asserts.assertTrue(!info.isWarmUp() && executed[1] == 101); + Asserts.assertTrue(!info.isWarmUp() && executed.getOrDefault("test2", 0) == 101); TestFramework.assertCompiledByC2(info.getTest()); } - @Test - public void overload() { - executed[4]++; - } - - @ForceCompile - @DontInline - public static void overload(int i) { - wasExecuted = true; - } - - @ForceCompile(CompLevel.C1_LIMITED_PROFILE) - @ForceInline - public static void overload(double i) { - wasExecuted = true; - } - - @Check(test = "overload") - public void checkOverload() { - executed[5]++; - } - @Test public void testDontCompile() { - executed[2]++; + executed.merge("testDontCompile", 1, Integer::sum); } @DontCompile public static void dontCompile() { - executed[3]++; + executed.merge("dontCompile", 1, Integer::sum); } @Run(test = "testDontCompile", mode = RunMode.STANDALONE) @@ -167,12 +136,12 @@ public class TestControls { @Test public void testCompileAtLevel1() { - executed[6]++; + executed.merge("testCompileAtLevel1", 1, Integer::sum); } @DontCompile(Compiler.ANY) public static void dontCompile2() { - executed[7]++; + executed.merge("dontCompile2", 1, Integer::sum); } @Run(test = "testCompileAtLevel1") @@ -181,23 +150,23 @@ public class TestControls { dontCompile2(); testCompileAtLevel1(); if (!info.isWarmUp()) { - executed[8]++; + executed.merge("runTestDontCompile2A", 1, Integer::sum); int compLevel = WHITE_BOX.getMethodCompilationLevel(TestControls.class.getDeclaredMethod("dontCompile2"), false); Asserts.assertLessThan(compLevel, CompLevel.C1_LIMITED_PROFILE.getValue()); } else { - executed[9]++; + executed.merge("runTestDontCompile2B", 1, Integer::sum); } } @Test @Warmup(0) public void noWarmup() { - executed[10]++; + executed.merge("noWarmup", 1, Integer::sum); } @Test public void noWarmup2() { - executed[11]++; + executed.merge("noWarmup2", 1, Integer::sum); } @Run(test = "noWarmup2") @@ -206,7 +175,7 @@ public class TestControls { noWarmup2(); noWarmup2(); Asserts.assertTrue(!info.isWarmUp()); - executed[12]++; + executed.merge("runNoWarmup2", 1, Integer::sum); } @Test @@ -301,7 +270,7 @@ public class TestControls { TestFramework.assertCompiledAtLevel(info.getTestClassMethod("forceC1DontC2"), CompLevel.C1_SIMPLE); TestFramework.assertCompiledAtLevel(info.getTestClassMethod("forceC2DontC1"), CompLevel.C2); - executed[13]++; + executed.merge("runTestCompilation", 1, Integer::sum); } } From e370b8a1d834a0a6ebcd1d5946a5533c015ed960 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 2 Feb 2026 10:32:51 +0000 Subject: [PATCH 080/215] 8376570: GrowableArray::remove_{till,range} should work on empty list Reviewed-by: kbarrett, iwalulya --- src/hotspot/share/utilities/growableArray.hpp | 12 +- .../gtest/utilities/test_growableArray.cpp | 117 +++++++++++++++++- 2 files changed, 122 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp index 1823a2ba861..e300bea6993 100644 --- a/src/hotspot/share/utilities/growableArray.hpp +++ b/src/hotspot/share/utilities/growableArray.hpp @@ -493,16 +493,16 @@ public: return false; } - // Remove all elements up to the index (exclusive). The order is preserved. - void remove_till(int idx) { - remove_range(0, idx); + // Remove all elements in the range [0; end). The order is preserved. + void remove_till(int end) { + remove_range(0, end); } - // Remove all elements in the range [start - end). The order is preserved. + // Remove all elements in the range [start; end). The order is preserved. void remove_range(int start, int end) { assert(0 <= start, "illegal start index %d", start); - assert(start < end && end <= this->_len, - "erase called with invalid range (%d, %d) for length %d", + assert(start <= end && end <= this->_len, + "erase called with invalid range [%d, %d) for length %d", start, end, this->_len); for (int i = start, j = end; j < this->length(); i++, j++) { diff --git a/test/hotspot/gtest/utilities/test_growableArray.cpp b/test/hotspot/gtest/utilities/test_growableArray.cpp index 45fc9498d27..6958da18ac3 100644 --- a/test/hotspot/gtest/utilities/test_growableArray.cpp +++ b/test/hotspot/gtest/utilities/test_growableArray.cpp @@ -232,12 +232,111 @@ protected: } } + template + static void test_remove_range(ArrayClass* a) { + // Seed initial + for (int i = 0; i < 10; i++) { + a->append(i); + } + ASSERT_EQ(a->length(), 10); + + // Remove empty range from the non-empty list, should not modify the list. + a->remove_range(0, 0); + ASSERT_EQ(a->length(), 10); + + // Remove one element from head, should result in [1 ... 9] + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 9); + for (int i = 0; i < a->length(); i++) { + ASSERT_EQ(a->at(i), i + 1); + } + + // Remove one element from tail, should result in [1 ... 8] + a->remove_range(8, 9); + ASSERT_EQ(a->length(), 8); + for (int i = 0; i < a->length(); i++) { + ASSERT_EQ(a->at(i), i + 1); + } + + // Remove another empty range from the non-empty list, should not modify + a->remove_range(1, 1); + ASSERT_EQ(a->length(), 8); + + // Remove some elements from the middle, should result in [1 2 7 8] + a->remove_range(2, 6); + ASSERT_EQ(a->length(), 4); + ASSERT_EQ(a->at(0), 1); + ASSERT_EQ(a->at(1), 2); + ASSERT_EQ(a->at(2), 7); + ASSERT_EQ(a->at(3), 8); + + // Remove the rest of the elements one by one + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 3); + ASSERT_EQ(a->at(0), 2); + ASSERT_EQ(a->at(1), 7); + ASSERT_EQ(a->at(2), 8); + + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 2); + ASSERT_EQ(a->at(0), 7); + ASSERT_EQ(a->at(1), 8); + + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 1); + ASSERT_EQ(a->at(0), 8); + + a->remove_range(0, 1); + ASSERT_EQ(a->length(), 0); + + // Remove elements from empty list with empty range, should be accepted + a->remove_range(0, 0); + ASSERT_EQ(a->length(), 0); + } + + template + static void test_remove_till(ArrayClass* a) { + // Seed initial + for (int i = 0; i < 10; i++) { + a->append(i); + } + ASSERT_EQ(a->length(), 10); + + // Remove empty range from non-empty list, should work + a->remove_till(0); + ASSERT_EQ(a->length(), 10); + + // Remove one element from head, should result in [1 ... 9] + a->remove_till(1); + ASSERT_EQ(a->length(), 9); + for (int i = 0; i < a->length(); i++) { + ASSERT_EQ(a->at(i), i + 1); + } + + // Remove two elements from head, should result in [3 ... 9] + a->remove_till(2); + ASSERT_EQ(a->length(), 7); + for (int i = 0; i < a->length(); i++) { + ASSERT_EQ(a->at(i), i + 3); + } + + // Remove remaining elements, should result in [] + a->remove_till(a->length()); + ASSERT_EQ(a->length(), 0); + + // Remove empty range from empty list, should work + a->remove_till(0); + ASSERT_EQ(a->length(), 0); + } + // Supported by all GrowableArrays enum TestEnum { Append, Clear, Capacity, - Iterator + Iterator, + RemoveRange, + RemoveTill }; template @@ -259,6 +358,14 @@ protected: test_iterator(a); break; + case RemoveRange: + test_remove_range(a); + break; + + case RemoveTill: + test_remove_till(a); + break; + default: fatal("Missing dispatch"); break; @@ -451,6 +558,14 @@ TEST_VM_F(GrowableArrayTest, iterator) { with_all_types_all_0(Iterator); } +TEST_VM_F(GrowableArrayTest, remove_range) { + with_all_types_all_0(RemoveRange); +} + +TEST_VM_F(GrowableArrayTest, remove_till) { + with_all_types_all_0(RemoveTill); +} + TEST_VM_F(GrowableArrayTest, copy) { with_no_cheap_array_append1(Copy1); } From 17f25b5ac46daed362f15005d65c5ee771328214 Mon Sep 17 00:00:00 2001 From: David Briemann Date: Mon, 2 Feb 2026 11:31:17 +0000 Subject: [PATCH 081/215] 8375536: PPC64: Implement special MachNodes for floating point CMove Reviewed-by: mdoerr, rrich --- src/hotspot/cpu/aarch64/aarch64.ad | 4 +- src/hotspot/cpu/ppc/assembler_ppc.hpp | 6 ++ src/hotspot/cpu/ppc/assembler_ppc.inline.hpp | 4 + src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp | 34 +++++++++ src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp | 2 + src/hotspot/cpu/ppc/matcher_ppc.hpp | 6 +- src/hotspot/cpu/ppc/ppc.ad | 76 ++++++++++++++++--- 7 files changed, 114 insertions(+), 18 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index b9252cc56ff..a9ca91d9309 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1229,7 +1229,7 @@ public: // predicate controlling addressing modes bool size_fits_all_mem_uses(AddPNode* addp, int shift); - // Convert BootTest condition to Assembler condition. + // Convert BoolTest condition to Assembler condition. // Replicate the logic of cmpOpOper::ccode() and cmpOpUOper::ccode(). Assembler::Condition to_assembler_cond(BoolTest::mask cond); %} @@ -2579,7 +2579,7 @@ bool size_fits_all_mem_uses(AddPNode* addp, int shift) { return true; } -// Convert BootTest condition to Assembler condition. +// Convert BoolTest condition to Assembler condition. // Replicate the logic of cmpOpOper::ccode() and cmpOpUOper::ccode(). Assembler::Condition to_assembler_cond(BoolTest::mask cond) { Assembler::Condition result; diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index 15e38411482..23775a3a52e 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -568,6 +568,9 @@ class Assembler : public AbstractAssembler { XSCVDPHP_OPCODE= (60u << OPCODE_SHIFT | 347u << 2 | 17u << 16), // XX2-FORM XXPERM_OPCODE = (60u << OPCODE_SHIFT | 26u << 3), XXSEL_OPCODE = (60u << OPCODE_SHIFT | 3u << 4), + XSCMPEQDP_OPCODE=(60u << OPCODE_SHIFT | 3u << 3), + XSCMPGEDP_OPCODE=(60u << OPCODE_SHIFT | 19u << 3), + XSCMPGTDP_OPCODE=(60u << OPCODE_SHIFT | 11u << 3), XXSPLTIB_OPCODE= (60u << OPCODE_SHIFT | 360u << 1), XVDIVDP_OPCODE = (60u << OPCODE_SHIFT | 120u << 3), XVABSSP_OPCODE = (60u << OPCODE_SHIFT | 409u << 2), @@ -2424,6 +2427,9 @@ class Assembler : public AbstractAssembler { inline void xscvdphp( VectorSRegister d, VectorSRegister b); inline void xxland( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xxsel( VectorSRegister d, VectorSRegister a, VectorSRegister b, VectorSRegister c); + inline void xscmpeqdp(VectorSRegister t, VectorSRegister a, VectorSRegister b); // Requires Power9 + inline void xscmpgedp(VectorSRegister t, VectorSRegister a, VectorSRegister b); // Requires Power9 + inline void xscmpgtdp(VectorSRegister t, VectorSRegister a, VectorSRegister b); // Requires Power9 inline void xxspltib( VectorSRegister d, int ui8); inline void xvdivsp( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xvdivdp( VectorSRegister d, VectorSRegister a, VectorSRegister b); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index 7e49ec7455d..4cda782067e 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -923,6 +923,10 @@ inline void Assembler::xxmrghw( VectorSRegister d, VectorSRegister a, VectorSReg inline void Assembler::xxmrglw( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXMRGHW_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xxsel( VectorSRegister d, VectorSRegister a, VectorSRegister b, VectorSRegister c) { emit_int32( XXSEL_OPCODE | vsrt(d) | vsra(a) | vsrb(b) | vsrc(c)); } +inline void Assembler::xscmpeqdp(VectorSRegister t, VectorSRegister a, VectorSRegister b) { emit_int32( XSCMPEQDP_OPCODE | vsrt(t) | vsra(a) | vsrb(b) );} +inline void Assembler::xscmpgedp(VectorSRegister t, VectorSRegister a, VectorSRegister b) { emit_int32( XSCMPGEDP_OPCODE | vsrt(t) | vsra(a) | vsrb(b) );} +inline void Assembler::xscmpgtdp(VectorSRegister t, VectorSRegister a, VectorSRegister b) { emit_int32( XSCMPGTDP_OPCODE | vsrt(t) | vsra(a) | vsrb(b) );} + // VSX Extended Mnemonics inline void Assembler::xxspltd( VectorSRegister d, VectorSRegister a, int x) { xxpermdi(d, a, a, x ? 3 : 0); } inline void Assembler::xxmrghd( VectorSRegister d, VectorSRegister a, VectorSRegister b) { xxpermdi(d, a, b, 0); } diff --git a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp index edf348fdc50..73b6b132895 100644 --- a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp @@ -664,3 +664,37 @@ void C2_MacroAssembler::reduceI(int opcode, Register dst, Register iSrc, VectorR fn_scalar_op(opcode, dst, iSrc, R0); // dst <- op(iSrc, R0) } +// Works for single and double precision floats. +// dst = (op1 cmp(cc) op2) ? src1 : src2; +// Unordered semantics are the same as for CmpF3Node/CmpD3Node which implement the fcmpl/dcmpl bytecodes. +// Comparing unordered values has the same result as when src1 is less than src2. +// So dst = src1 for <, <=, != and dst = src2 for >, >=, ==. +void C2_MacroAssembler::cmovF(int cc, VectorSRegister dst, VectorSRegister op1, VectorSRegister op2, + VectorSRegister src1, VectorSRegister src2, VectorSRegister tmp) { + // See operand cmpOp() for details. + bool invert_cond = (cc & 8) == 0; // invert reflects bcondCRbiIs0 + auto cmp = (Assembler::Condition)(cc & 3); + + switch(cmp) { + case Assembler::Condition::equal: + // Use false_result if "unordered". + xscmpeqdp(tmp, op1, op2); + break; + case Assembler::Condition::greater: + // Use false_result if "unordered". + xscmpgtdp(tmp, op1, op2); + break; + case Assembler::Condition::less: + // Use true_result if "unordered". + xscmpgedp(tmp, op1, op2); + invert_cond = !invert_cond; + break; + default: + assert(false, "unsupported compare condition: %d", cc); + ShouldNotReachHere(); + } + + VectorSRegister true_result = invert_cond ? src2 : src1; + VectorSRegister false_result = invert_cond ? src1 : src2; + xxsel(dst, false_result, true_result, tmp); +} diff --git a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp index 5a114294c1f..e0dffec8396 100644 --- a/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp @@ -74,5 +74,7 @@ void count_positives(Register src, Register cnt, Register result, Register tmp1, Register tmp2); void reduceI(int opcode, Register dst, Register iSrc, VectorRegister vSrc, VectorRegister vTmp1, VectorRegister vTmp2); + void cmovF(int cc, VectorSRegister dst, VectorSRegister op1, VectorSRegister op2, + VectorSRegister src1, VectorSRegister src2, VectorSRegister tmp); #endif // CPU_PPC_C2_MACROASSEMBLER_PPC_HPP diff --git a/src/hotspot/cpu/ppc/matcher_ppc.hpp b/src/hotspot/cpu/ppc/matcher_ppc.hpp index aad41fb7b1c..b50de6323de 100644 --- a/src/hotspot/cpu/ppc/matcher_ppc.hpp +++ b/src/hotspot/cpu/ppc/matcher_ppc.hpp @@ -64,12 +64,10 @@ return true; } - // Use conditional move (CMOVL) on Power7. static constexpr int long_cmove_cost() { return 0; } // this only makes long cmoves more expensive than int cmoves - // Suppress CMOVF. Conditional move available (sort of) on PPC64 only from P7 onwards. Not exploited yet. - // fsel doesn't accept a condition register as input, so this would be slightly different. - static int float_cmove_cost() { return ConditionalMoveLimit; } + // Suppress CMOVF for Power8 because there are no fast nodes. + static int float_cmove_cost() { return (PowerArchitecturePPC64 >= 9) ? 0 : ConditionalMoveLimit; } // This affects two different things: // - how Decode nodes are matched diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 2a0a9149bb3..d926fabd353 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3024,7 +3024,6 @@ encode %{ %} enc_class postalloc_expand_encode_oop(iRegNdst dst, iRegPdst src, flagsReg crx) %{ - // use isel instruction with Power 7 cmpP_reg_imm16Node *n_compare = new cmpP_reg_imm16Node(); encodeP_subNode *n_sub_base = new encodeP_subNode(); encodeP_shiftNode *n_shift = new encodeP_shiftNode(); @@ -3099,7 +3098,6 @@ encode %{ n_shift->_opnds[1] = op_src; n_shift->_bottom_type = _bottom_type; - // use isel instruction with Power 7 decodeN_addNode *n_add_base = new decodeN_addNode(); n_add_base->add_req(n_region, n_shift); n_add_base->_opnds[0] = op_dst; @@ -6618,7 +6616,6 @@ instruct cond_sub_base(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{ ins_pipe(pipe_class_default); %} -// Power 7 can use isel instruction instruct cond_set_0_oop(iRegNdst dst, flagsRegSrc crx, iRegPsrc src1) %{ // The match rule is needed to make it a 'MachTypeNode'! match(Set dst (EncodeP (Binary crx src1))); @@ -7293,7 +7290,6 @@ instruct cmovF_reg(cmpOp cmp, flagsRegSrc crx, regF dst, regF src) %{ ins_variable_size_depending_on_alignment(true); format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode %{ Label done; @@ -7313,7 +7309,6 @@ instruct cmovD_reg(cmpOp cmp, flagsRegSrc crx, regD dst, regD src) %{ ins_variable_size_depending_on_alignment(true); format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode %{ Label done; @@ -7326,6 +7321,70 @@ instruct cmovD_reg(cmpOp cmp, flagsRegSrc crx, regD dst, regD src) %{ ins_pipe(pipe_class_default); %} +instruct cmovF_cmpF(cmpOp cop, regF op1, regF op2, regF dst, regF false_result, regF true_result, regD tmp) %{ + match(Set dst (CMoveF (Binary cop (CmpF op1 op2)) (Binary false_result true_result))); + predicate(PowerArchitecturePPC64 >= 9); + effect(TEMP tmp); + ins_cost(2*DEFAULT_COST); + format %{ "cmovF_cmpF $dst = ($op1 $cop $op2) ? $true_result : $false_result\n\t" %} + size(8); + ins_encode %{ + __ cmovF($cop$$cmpcode, $dst$$FloatRegister->to_vsr(), + $op1$$FloatRegister->to_vsr(), $op2$$FloatRegister->to_vsr(), + $true_result$$FloatRegister->to_vsr(), $false_result$$FloatRegister->to_vsr(), + $tmp$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct cmovF_cmpD(cmpOp cop, regD op1, regD op2, regF dst, regF false_result, regF true_result, regD tmp) %{ + match(Set dst (CMoveF (Binary cop (CmpD op1 op2)) (Binary false_result true_result))); + predicate(PowerArchitecturePPC64 >= 9); + effect(TEMP tmp); + ins_cost(2*DEFAULT_COST); + format %{ "cmovF_cmpD $dst = ($op1 $cop $op2) ? $true_result : $false_result\n\t" %} + size(8); + ins_encode %{ + __ cmovF($cop$$cmpcode, $dst$$FloatRegister->to_vsr(), + $op1$$FloatRegister->to_vsr(), $op2$$FloatRegister->to_vsr(), + $true_result$$FloatRegister->to_vsr(), $false_result$$FloatRegister->to_vsr(), + $tmp$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct cmovD_cmpD(cmpOp cop, regD op1, regD op2, regD dst, regD false_result, regD true_result, regD tmp) %{ + match(Set dst (CMoveD (Binary cop (CmpD op1 op2)) (Binary false_result true_result))); + predicate(PowerArchitecturePPC64 >= 9); + effect(TEMP tmp); + ins_cost(2*DEFAULT_COST); + format %{ "cmovD_cmpD $dst = ($op1 $cop $op2) ? $true_result : $false_result\n\t" %} + size(8); + ins_encode %{ + __ cmovF($cop$$cmpcode, $dst$$FloatRegister->to_vsr(), + $op1$$FloatRegister->to_vsr(), $op2$$FloatRegister->to_vsr(), + $true_result$$FloatRegister->to_vsr(), $false_result$$FloatRegister->to_vsr(), + $tmp$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + +instruct cmovD_cmpF(cmpOp cop, regF op1, regF op2, regD dst, regD false_result, regD true_result, regD tmp) %{ + match(Set dst (CMoveD (Binary cop (CmpF op1 op2)) (Binary false_result true_result))); + predicate(PowerArchitecturePPC64 >= 9); + effect(TEMP tmp); + ins_cost(2*DEFAULT_COST); + format %{ "cmovD_cmpF $dst = ($op1 $cop $op2) ? $true_result : $false_result\n\t" %} + size(8); + ins_encode %{ + __ cmovF($cop$$cmpcode, $dst$$FloatRegister->to_vsr(), + $op1$$FloatRegister->to_vsr(), $op2$$FloatRegister->to_vsr(), + $true_result$$FloatRegister->to_vsr(), $false_result$$FloatRegister->to_vsr(), + $tmp$$FloatRegister->to_vsr()); + %} + ins_pipe(pipe_class_default); +%} + //----------Compare-And-Swap--------------------------------------------------- // CompareAndSwap{P,I,L} have more than one output, therefore "CmpI @@ -8492,7 +8551,6 @@ instruct cmovI_bne_negI_reg(iRegIdst dst, flagsRegSrc crx, iRegIsrc src1) %{ ins_variable_size_depending_on_alignment(true); format %{ "CMOVE $dst, neg($src1), $crx" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode %{ Label done; @@ -8551,7 +8609,6 @@ instruct cmovL_bne_negL_reg(iRegLdst dst, flagsRegSrc crx, iRegLsrc src1) %{ ins_variable_size_depending_on_alignment(true); format %{ "CMOVE $dst, neg($src1), $crx" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode %{ Label done; @@ -10262,7 +10319,6 @@ instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmovI $crx, $dst, $src" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10276,7 +10332,6 @@ instruct cmovI_bso_reg(iRegIdst dst, flagsRegSrc crx, regD src) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmovI $crx, $dst, $src" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10439,7 +10494,6 @@ instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmovL $crx, $dst, $src" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -10453,7 +10507,6 @@ instruct cmovL_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmovL $crx, $dst, $src" %} - // Worst case is branch + move + stop, no stop without scheduler. size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); ins_pipe(pipe_class_default); @@ -11080,7 +11133,6 @@ instruct cmov_bns_less(flagsReg crx) %{ ins_variable_size_depending_on_alignment(true); format %{ "cmov $crx" %} - // Worst case is branch + move + stop, no stop without scheduler. size(12); ins_encode %{ Label done; From 176422b885d2d045dd44b61b7fcdcb01be2d00a7 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Mon, 2 Feb 2026 11:43:30 +0000 Subject: [PATCH 082/215] 8370519: C2: Hit MemLimit when running with +VerifyLoopOptimizations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Benoît Maillard Reviewed-by: mhaessig, bmaillard, epeter --- src/hotspot/share/memory/arena.hpp | 3 +- src/hotspot/share/opto/loopnode.cpp | 24 +++- src/hotspot/share/opto/loopnode.hpp | 28 ++-- ...stVerifyLoopOptimizationsHighMemUsage.java | 126 ++++++++++++++++++ 4 files changed, 157 insertions(+), 24 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/TestVerifyLoopOptimizationsHighMemUsage.java diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index a8450b5543a..7d88c79ca52 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.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 @@ -101,6 +101,7 @@ public: FN(ra, Resource areas) \ FN(node, C2 Node arena) \ FN(comp, C2 Compile arena) \ + FN(idealloop, C2 Ideal Loop arena) \ FN(type, C2 Type arena) \ FN(states, C2 Matcher States Arena) \ FN(reglive, C2 Register Allocation Live Arenas) \ diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index fab354e3e3d..d68505836d4 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -3752,6 +3752,20 @@ void CountedLoopEndNode::dump_spec(outputStream *st) const { } #endif +IdealLoopTree::IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail): _parent(nullptr), _next(nullptr), _child(nullptr), + _head(head), _tail(tail), + _phase(phase), + _local_loop_unroll_limit(0), _local_loop_unroll_factor(0), + _body(phase->arena()), + _nest(0), _irreducible(0), _has_call(0), _has_sfpt(0), _rce_candidate(0), + _has_range_checks(0), _has_range_checks_computed(0), + _safepts(nullptr), + _required_safept(nullptr), + _allow_optimizations(true) { + precond(_head != nullptr); + precond(_tail != nullptr); +} + //============================================================================= //------------------------------is_member-------------------------------------- // Is 'l' a member of 'this'? @@ -5089,8 +5103,8 @@ void PhaseIdealLoop::build_and_optimize() { // Since nodes do not have a slot for immediate dominator, make // a persistent side array for that info indexed on node->_idx. _idom_size = C->unique(); - _idom = NEW_RESOURCE_ARRAY( Node*, _idom_size ); - _dom_depth = NEW_RESOURCE_ARRAY( uint, _idom_size ); + _idom = NEW_ARENA_ARRAY(&_arena, Node*, _idom_size); + _dom_depth = NEW_ARENA_ARRAY(&_arena, uint, _idom_size); _dom_stk = nullptr; // Allocated on demand in recompute_dom_depth memset( _dom_depth, 0, _idom_size * sizeof(uint) ); @@ -5691,8 +5705,8 @@ void PhaseIdealLoop::set_idom(Node* d, Node* n, uint dom_depth) { uint idx = d->_idx; if (idx >= _idom_size) { uint newsize = next_power_of_2(idx); - _idom = REALLOC_RESOURCE_ARRAY( Node*, _idom,_idom_size,newsize); - _dom_depth = REALLOC_RESOURCE_ARRAY( uint, _dom_depth,_idom_size,newsize); + _idom = REALLOC_ARENA_ARRAY(&_arena, Node*, _idom,_idom_size,newsize); + _dom_depth = REALLOC_ARENA_ARRAY(&_arena, uint, _dom_depth,_idom_size,newsize); memset( _dom_depth + _idom_size, 0, (newsize - _idom_size) * sizeof(uint) ); _idom_size = newsize; } diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 24976d76a51..5b06f0555ab 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -669,21 +669,7 @@ public: Node_List* _required_safept; // A inner loop cannot delete these safepts; bool _allow_optimizations; // Allow loop optimizations - IdealLoopTree( PhaseIdealLoop* phase, Node *head, Node *tail ) - : _parent(nullptr), _next(nullptr), _child(nullptr), - _head(head), _tail(tail), - _phase(phase), - _local_loop_unroll_limit(0), _local_loop_unroll_factor(0), - _body(Compile::current()->comp_arena()), - _nest(0), _irreducible(0), _has_call(0), _has_sfpt(0), _rce_candidate(0), - _has_range_checks(0), _has_range_checks_computed(0), - _safepts(nullptr), - _required_safept(nullptr), - _allow_optimizations(true) - { - precond(_head != nullptr); - precond(_tail != nullptr); - } + IdealLoopTree(PhaseIdealLoop* phase, Node* head, Node* tail); // Is 'l' a member of 'this'? bool is_member(const IdealLoopTree *l) const; // Test for nested membership @@ -889,6 +875,8 @@ class PhaseIdealLoop : public PhaseTransform { friend class ShenandoahBarrierC2Support; friend class AutoNodeBudget; + Arena _arena; // For data whose lifetime is a single pass of loop optimizations + // Map loop membership for CFG nodes, and ctrl for non-CFG nodes. // // Exception: dead CFG nodes may instead have a ctrl/idom forwarding @@ -1049,6 +1037,8 @@ public: PhaseIterGVN &igvn() const { return _igvn; } + Arena* arena() { return &_arena; }; + bool has_node(const Node* n) const { guarantee(n != nullptr, "No Node."); return _loop_or_ctrl[n->_idx] != nullptr; @@ -1223,7 +1213,8 @@ private: // Compute the Ideal Node to Loop mapping PhaseIdealLoop(PhaseIterGVN& igvn, LoopOptsMode mode) : PhaseTransform(Ideal_Loop), - _loop_or_ctrl(igvn.C->comp_arena()), + _arena(mtCompiler, Arena::Tag::tag_idealloop), + _loop_or_ctrl(&_arena), _igvn(igvn), _verify_me(nullptr), _verify_only(false), @@ -1238,7 +1229,8 @@ private: // or only verify that the graph is valid if verify_me is null. PhaseIdealLoop(PhaseIterGVN& igvn, const PhaseIdealLoop* verify_me = nullptr) : PhaseTransform(Ideal_Loop), - _loop_or_ctrl(igvn.C->comp_arena()), + _arena(mtCompiler, Arena::Tag::tag_idealloop), + _loop_or_ctrl(&_arena), _igvn(igvn), _verify_me(verify_me), _verify_only(verify_me == nullptr), diff --git a/test/hotspot/jtreg/compiler/c2/TestVerifyLoopOptimizationsHighMemUsage.java b/test/hotspot/jtreg/compiler/c2/TestVerifyLoopOptimizationsHighMemUsage.java new file mode 100644 index 00000000000..ea7f3049ec3 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestVerifyLoopOptimizationsHighMemUsage.java @@ -0,0 +1,126 @@ +/* + * 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 + * @key stress randomness + * @bug 8370519 + * @summary C2: Hit MemLimit when running with +VerifyLoopOptimizations + * @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}::* -XX:-TieredCompilation -Xbatch + * -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions + * -XX:+StressLoopPeeling -XX:+VerifyLoopOptimizations + * -XX:CompileCommand=memlimit,${test.main.class}::*,600M~crash + * -XX:StressSeed=3106998670 ${test.main.class} + * @run main ${test.main.class} + */ + +package compiler.c2; + +public class TestVerifyLoopOptimizationsHighMemUsage { + public static final int N = 400; + public static long instanceCount = -13L; + public static volatile short sFld = -16143; + public static int iFld = -159; + public static float fArrFld[] = new float[N]; + + public static long lMeth(int i1) { + int i2 = 11, i3 = 37085, i4 = 177, i5 = 190, i6 = -234, i7 = 13060, + iArr[] = new int[N]; + float f = 1.179F; + double d = 2.9685; + long lArr[] = new long[N]; + for (i2 = 15; i2 < 330; ++i2) + for (i4 = 1; i4 < 5; ++i4) { + fArrFld[i4 + 1] = (++i1); + for (i6 = 2; i6 > 1; i6 -= 3) + switch ((i2 * 5) + 54) { + case 156: + if (i4 != 0) + ; + case 168: + case 342: + case 283: + case 281: + case 328: + case 322: + case 228: + case 114: + case 207: + case 209: + case 354: + case 108: + i1 <<= i1; + case 398: + case 144: + case 218: + case 116: + case 296: + case 198: + case 173: + case 105: + case 120: + case 248: + case 140: + case 352: + try { + } catch (ArithmeticException a_e) { + } + case 404: + i5 += (i6 ^ instanceCount); + case 370: + case 211: + case 231: + try { + } catch (ArithmeticException a_e) { + } + case 251: + case 179: + f += (((i6 * sFld) + i4) - + iFld); + } + } + long meth_res = i1 + i2 + i3 + i4 + i5 + i6 + i7 + Float.floatToIntBits(f) + + Double.doubleToLongBits(d) + +checkSum(iArr) + + checkSum(lArr); + return meth_res; + } + + public static long checkSum(int[] a) { + long sum = 0; + for (int j = 0; j < a.length; j++) + sum += (a[j] / (j + 1) + a[j] % (j + 1)); + return sum; + } + + public static long checkSum(long[] a) { + long sum = 0; + for (int j = 0; j < a.length; j++) + sum += (a[j] / (j + 1) + a[j] % (j + 1)); + return sum; + } + + public static void main(String[] strArr) { + for (int i = 0; i < 10; i++) + lMeth(-159); + } +} From 173c3f9852672f6c917e975383172c8878ba7e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?= Date: Mon, 2 Feb 2026 14:57:14 +0000 Subject: [PATCH 083/215] 8376479: Http3 test server thread deadlock in ThrowingPublishersInRequest Co-authored-by: Volkan Yazici Reviewed-by: dfuchs --- .../test/lib/http3/Http3ServerExchange.java | 4 +- .../test/lib/http3/Http3ServerStreamImpl.java | 87 ++++++++----------- 2 files changed, 36 insertions(+), 55 deletions(-) diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerExchange.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerExchange.java index c127abf3c0f..1c61f00d164 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerExchange.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerExchange.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 @@ -175,7 +175,7 @@ public final class Http3ServerExchange implements Http2TestExchange { } serverStream.writer.reset(Http3Error.H3_INTERNAL_ERROR.code()); } - is.close(io); + is.resetStream(io); os.closeInternal(); close(); } diff --git a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerStreamImpl.java b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerStreamImpl.java index c5a8709346c..ebe92fa3bac 100644 --- a/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerStreamImpl.java +++ b/test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http3/Http3ServerStreamImpl.java @@ -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 @@ -32,6 +32,7 @@ import java.util.Objects; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; @@ -264,7 +265,7 @@ final class Http3ServerStreamImpl { // nothing to do - let the response be sent to the client, but throw an // exception if `is` is used again. exchangeCF.thenApply(en -> { - en.is.close(new IOException("stopSendingRequested")); + en.is.resetStream(new IOException("stopSendingRequested")); return en; }); return; @@ -292,38 +293,37 @@ final class Http3ServerStreamImpl { } class RequestBodyInputStream extends InputStream { - volatile IOException error; - volatile boolean closed; + // Non-null if the stream is terminated. + // Points to an IOException on error, or Boolean.TRUE on EOF. + private final AtomicReference closeReason = new AtomicReference<>(); // uses an unbounded blocking queue in which the readrLoop // publishes the DataFrames payload... ByteBuffer current; - // Use lock to avoid pinned threads on the blocking queue - final ReentrantLock lock = new ReentrantLock(); ByteBuffer current() throws IOException { - lock.lock(); - try { - while (true) { - if (current != null && current.hasRemaining()) { - return current; - } - if (current == QuicStreamReader.EOF) return current; - try { - if (debug.on()) - debug.log("Taking buffer from queue"); - // Blocking call - current = requestBodyQueue.take(); - } catch (InterruptedException e) { - var io = new InterruptedIOException(); - Thread.currentThread().interrupt(); - io.initCause(e); - close(io); - var error = this.error; - if (error != null) throw error; + while (true) { + Object reason = closeReason.get(); + if (reason != null) { + if (reason == Boolean.TRUE) { + throw new IOException("Stream is closed"); + } else { + throw new IOException((IOException)reason); } } - } finally { - lock.unlock(); + if (current != null && (current.hasRemaining() || current == QuicStreamReader.EOF)) { + return current; + } + try { + if (debug.on()) + debug.log("Taking buffer from queue"); + // Blocking call + current = requestBodyQueue.take(); + } catch (InterruptedException e) { + var io = new InterruptedIOException(); + Thread.currentThread().interrupt(); + io.initCause(e); + throw io; + } } } @@ -331,9 +331,7 @@ final class Http3ServerStreamImpl { public int read() throws IOException { ByteBuffer buffer = current(); if (buffer == QuicStreamReader.EOF) { - var error = this.error; - if (error == null) return -1; - throw error; + return -1; } return buffer.get() & 0xFF; } @@ -345,11 +343,7 @@ final class Http3ServerStreamImpl { while (remaining > 0) { ByteBuffer buffer = current(); if (buffer == QuicStreamReader.EOF) { - if (len == remaining) { - var error = this.error; - if (error == null) return -1; - throw error; - } else return len - remaining; + return len == remaining ? -1 : len - remaining; } int count = Math.min(buffer.remaining(), remaining); buffer.get(b, off + (len - remaining), count); @@ -360,33 +354,20 @@ final class Http3ServerStreamImpl { @Override public void close() throws IOException { - lock.lock(); - try { - if (closed) return; - closed = true; - - } finally { - lock.unlock(); - } + if (closeReason.getAndSet(Boolean.TRUE) == Boolean.TRUE) return; if (debug.on()) debug.log("Closing request body input stream"); requestBodyQueue.add(QuicStreamReader.EOF); + stream.requestStopSending(Http3Error.H3_NO_ERROR.code()); } - void close(IOException io) { - lock.lock(); - try { - if (closed) return; - closed = true; - error = io; - } finally { - lock.unlock(); - } + void resetStream(IOException io) { + if (!closeReason.compareAndSet(null, io)) return; if (debug.on()) { debug.log("Closing request body input stream: " + io); } - requestBodyQueue.clear(); requestBodyQueue.add(QuicStreamReader.EOF); + stream.requestStopSending(Http3Error.H3_NO_ERROR.code()); } } From b7128b7c30f3de2c1dcee2be567bb25d407c71a2 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 2 Feb 2026 15:16:35 +0000 Subject: [PATCH 084/215] 8376357: Parallel: Convert MutableSpace classes to use Atomic Reviewed-by: dholmes, iwalulya --- .../share/gc/parallel/mutableNUMASpace.cpp | 6 ++--- .../share/gc/parallel/mutableSpace.cpp | 17 +++++--------- .../share/gc/parallel/mutableSpace.hpp | 22 +++++++++---------- .../gc/parallel/vmStructs_parallelgc.hpp | 4 ++-- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp index e0b1edf2efc..c5d112ffbc1 100644 --- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp @@ -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. * 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,7 +31,7 @@ #include "memory/allocation.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/java.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.inline.hpp" @@ -489,7 +489,7 @@ HeapWord* MutableNUMASpace::cas_allocate(size_t size) { if (p != nullptr) { HeapWord* cur_top, *cur_chunk_top = p + size; while ((cur_top = top()) < cur_chunk_top) { // Keep _top updated. - if (AtomicAccess::cmpxchg(top_addr(), cur_top, cur_chunk_top) == cur_top) { + if (top_addr()->compare_set(cur_top, cur_chunk_top)) { break; } } diff --git a/src/hotspot/share/gc/parallel/mutableSpace.cpp b/src/hotspot/share/gc/parallel/mutableSpace.cpp index fc42fc1eab2..d99db493989 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.cpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.cpp @@ -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 @@ -28,7 +28,6 @@ #include "memory/iterator.inline.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/javaThread.hpp" #include "runtime/safepoint.hpp" #include "utilities/align.hpp" @@ -123,7 +122,7 @@ void MutableSpace::initialize(MemRegion mr, // makes the new space available for allocation by other threads. So this // assignment must follow all other configuration and initialization that // might be done for expansion. - AtomicAccess::release_store(end_addr(), mr.end()); + _end.release_store(mr.end()); if (clear_space) { clear(mangle_space); @@ -140,7 +139,7 @@ void MutableSpace::clear(bool mangle_space) { #ifndef PRODUCT void MutableSpace::mangle_unused_area() { - mangle_region(MemRegion(_top, _end)); + mangle_region(MemRegion(top(), end())); } void MutableSpace::mangle_region(MemRegion mr) { @@ -155,14 +154,10 @@ HeapWord* MutableSpace::cas_allocate(size_t size) { // If end is read first, other threads may advance end and top such that // current top > old end and current top + size > current end. Then // pointer_delta underflows, allowing installation of top > current end. - HeapWord* obj = AtomicAccess::load_acquire(top_addr()); + HeapWord* obj = _top.load_acquire(); if (pointer_delta(end(), obj) >= size) { HeapWord* new_top = obj + size; - HeapWord* result = AtomicAccess::cmpxchg(top_addr(), obj, new_top); - // result can be one of two: - // the old top value: the exchange succeeded - // otherwise: the new value of the top is returned. - if (result != obj) { + if (!_top.compare_set(obj, new_top)) { continue; // another thread beat us to the allocation, try again } assert(is_object_aligned(obj) && is_object_aligned(new_top), @@ -177,7 +172,7 @@ HeapWord* MutableSpace::cas_allocate(size_t size) { // Try to deallocate previous allocation. Returns true upon success. bool MutableSpace::cas_deallocate(HeapWord *obj, size_t size) { HeapWord* expected_top = obj + size; - return AtomicAccess::cmpxchg(top_addr(), expected_top, obj) == expected_top; + return _top.compare_set(expected_top, obj); } void MutableSpace::oop_iterate(OopIterateClosure* cl) { diff --git a/src/hotspot/share/gc/parallel/mutableSpace.hpp b/src/hotspot/share/gc/parallel/mutableSpace.hpp index 9d3894e2489..28df19a7c4b 100644 --- a/src/hotspot/share/gc/parallel/mutableSpace.hpp +++ b/src/hotspot/share/gc/parallel/mutableSpace.hpp @@ -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 @@ -28,6 +28,7 @@ #include "memory/allocation.hpp" #include "memory/iterator.hpp" #include "memory/memRegion.hpp" +#include "runtime/atomic.hpp" #include "utilities/copy.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" @@ -53,8 +54,8 @@ class MutableSpace: public CHeapObj { MemRegion _last_setup_region; size_t _page_size; HeapWord* _bottom; - HeapWord* volatile _top; - HeapWord* _end; + Atomic _top; + Atomic _end; void numa_setup_pages(MemRegion mr, bool clear_space); @@ -64,21 +65,20 @@ class MutableSpace: public CHeapObj { protected: size_t page_size() const { return _page_size; } + Atomic* top_addr() { return &_top; } + public: virtual ~MutableSpace() = default; MutableSpace(size_t page_size); // Accessors HeapWord* bottom() const { return _bottom; } - HeapWord* top() const { return _top; } - HeapWord* end() const { return _end; } + HeapWord* top() const { return _top.load_relaxed(); } + HeapWord* end() const { return _end.load_relaxed(); } void set_bottom(HeapWord* value) { _bottom = value; } - virtual void set_top(HeapWord* value) { _top = value; } - void set_end(HeapWord* value) { _end = value; } - - HeapWord* volatile* top_addr() { return &_top; } - HeapWord** end_addr() { return &_end; } + virtual void set_top(HeapWord* value) { _top.store_relaxed(value); } + void set_end(HeapWord* value) { _end.store_relaxed(value); } MemRegion region() const { return MemRegion(bottom(), end()); } @@ -110,7 +110,7 @@ public: // Boolean queries. bool is_empty() const { return used_in_words() == 0; } bool not_empty() const { return used_in_words() > 0; } - bool contains(const void* p) const { return _bottom <= p && p < _end; } + bool contains(const void* p) const { return _bottom <= p && p < end(); } // Size computations. Sizes are in bytes. size_t used_in_bytes() const { return used_in_words() * HeapWordSize; } diff --git a/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp b/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp index f69219a1f40..f8dabc4539e 100644 --- a/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp +++ b/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp @@ -47,8 +47,8 @@ nonstatic_field(PSVirtualSpace, _committed_high_addr, char*) \ \ nonstatic_field(MutableSpace, _bottom, HeapWord*) \ - nonstatic_field(MutableSpace, _end, HeapWord*) \ - volatile_nonstatic_field(MutableSpace, _top, HeapWord*) \ + nonstatic_field(MutableSpace, _end, Atomic) \ + volatile_nonstatic_field(MutableSpace, _top, Atomic) \ \ nonstatic_field(PSYoungGen, _reserved, MemRegion) \ nonstatic_field(PSYoungGen, _virtual_space, PSVirtualSpace*) \ From 903b3fe19596adaeac7cfb0d749b6e83f668f52f Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 2 Feb 2026 15:19:15 +0000 Subject: [PATCH 085/215] 8375438: G1: Convert G1HeapRegion related classes to use Atomic Reviewed-by: shade, iwalulya --- src/hotspot/share/gc/g1/g1HeapRegion.cpp | 14 +++---- src/hotspot/share/gc/g1/g1HeapRegion.hpp | 21 ++++++----- .../share/gc/g1/g1HeapRegion.inline.hpp | 37 ++++++++++++------- .../share/gc/g1/g1HeapRegionManager.cpp | 13 ++++--- .../share/gc/g1/g1HeapRegionManager.hpp | 5 ++- src/hotspot/share/gc/g1/g1YoungCollector.cpp | 1 + src/hotspot/share/gc/g1/vmStructs_g1.hpp | 5 ++- src/hotspot/share/runtime/vmStructs.cpp | 2 + 8 files changed, 57 insertions(+), 41 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 361e19d4be5..2052a3ce156 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -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 @@ -44,7 +44,7 @@ #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals_extension.hpp" #include "utilities/powerOfTwo.hpp" @@ -131,8 +131,8 @@ void G1HeapRegion::hr_clear(bool clear_space) { G1CollectedHeap::heap()->concurrent_mark()->reset_top_at_mark_start(this); - _parsable_bottom = bottom(); - _garbage_bytes = 0; + _parsable_bottom.store_relaxed(bottom()); + _garbage_bytes.store_relaxed(0); _incoming_refs = 0; if (clear_space) clear(SpaceDecorator::Mangle); @@ -294,12 +294,12 @@ void G1HeapRegion::report_region_type_change(G1HeapRegionTraceType::Type to) { // young gen regions never have their PB set to anything other than bottom. assert(parsable_bottom_acquire() == bottom(), "must be"); - _garbage_bytes = 0; + _garbage_bytes.store_relaxed(0); _incoming_refs = 0; } void G1HeapRegion::note_self_forward_chunk_done(size_t garbage_bytes) { - AtomicAccess::add(&_garbage_bytes, garbage_bytes, memory_order_relaxed); + _garbage_bytes.add_then_fetch(garbage_bytes, memory_order_relaxed); } // Code roots support @@ -448,7 +448,7 @@ void G1HeapRegion::print_on(outputStream* st) const { st->print("|-"); } } - st->print("|%3zu", AtomicAccess::load(&_pinned_object_count)); + st->print("|%3zu", _pinned_object_count.load_relaxed()); st->print_cr(""); } diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.hpp index fe915b0dafe..2b4b640d52b 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.hpp @@ -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 @@ -33,6 +33,7 @@ #include "gc/shared/ageTable.hpp" #include "gc/shared/spaceDecorator.hpp" #include "gc/shared/verifyOption.hpp" +#include "runtime/atomic.hpp" #include "runtime/mutex.hpp" #include "utilities/macros.hpp" @@ -73,7 +74,7 @@ class G1HeapRegion : public CHeapObj { HeapWord* const _bottom; HeapWord* const _end; - HeapWord* volatile _top; + Atomic _top; G1BlockOffsetTable* _bot; @@ -89,8 +90,8 @@ public: HeapWord* bottom() const { return _bottom; } HeapWord* end() const { return _end; } - void set_top(HeapWord* value) { _top = value; } - HeapWord* top() const { return _top; } + void set_top(HeapWord* value) { _top.store_relaxed(value); } + HeapWord* top() const { return _top.load_relaxed(); } // See the comment above in the declaration of _pre_dummy_top for an // explanation of what it is. @@ -231,10 +232,10 @@ private: // // Below this limit the marking bitmap must be used to determine size and // liveness. - HeapWord* volatile _parsable_bottom; + Atomic _parsable_bottom; // Amount of dead data in the region. - size_t _garbage_bytes; + Atomic _garbage_bytes; // Approximate number of references to this regions at the end of concurrent // marking. We we do not mark through all objects, so this is an estimate. @@ -249,7 +250,7 @@ private: uint _node_index; // Number of objects in this region that are currently pinned. - volatile size_t _pinned_object_count; + Atomic _pinned_object_count; void report_region_type_change(G1HeapRegionTraceType::Type to); @@ -331,7 +332,7 @@ public: } // A lower bound on the amount of garbage bytes in the region. - size_t garbage_bytes() const { return _garbage_bytes; } + size_t garbage_bytes() const { return _garbage_bytes.load_relaxed(); } // Return the amount of bytes we'll reclaim if we collect this // region. This includes not only the known garbage bytes in the @@ -393,8 +394,8 @@ public: bool is_old_or_humongous() const { return _type.is_old_or_humongous(); } - size_t pinned_count() const { return AtomicAccess::load(&_pinned_object_count); } - bool has_pinned_objects() const { return pinned_count() > 0; } + inline size_t pinned_count() const; + inline bool has_pinned_objects() const; void set_free(); diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp index f25bf62c9be..4f242b7a537 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.inline.hpp @@ -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 @@ -35,7 +35,6 @@ #include "gc/g1/g1Policy.hpp" #include "gc/g1/g1Predictions.hpp" #include "oops/oop.inline.hpp" -#include "runtime/atomicAccess.hpp" #include "runtime/init.hpp" #include "runtime/prefetch.inline.hpp" #include "runtime/safepoint.hpp" @@ -131,7 +130,7 @@ inline void G1HeapRegion::prepare_for_full_gc() { // After marking and class unloading the heap temporarily contains dead objects // with unloaded klasses. Moving parsable_bottom makes some (debug) code correctly // skip dead objects. - _parsable_bottom = top(); + _parsable_bottom.store_relaxed(top()); } inline void G1HeapRegion::reset_compacted_after_full_gc(HeapWord* new_top) { @@ -154,7 +153,7 @@ inline void G1HeapRegion::reset_after_full_gc_common() { // Everything above bottom() is parsable and live. reset_parsable_bottom(); - _garbage_bytes = 0; + _garbage_bytes.store_relaxed(0); _incoming_refs = 0; @@ -188,20 +187,22 @@ inline void G1HeapRegion::apply_to_marked_objects(G1CMBitMap* bitmap, ApplyToMar inline HeapWord* G1HeapRegion::par_allocate(size_t min_word_size, size_t desired_word_size, size_t* actual_word_size) { + HeapWord* obj = top(); do { - HeapWord* obj = top(); size_t available = pointer_delta(end(), obj); size_t want_to_allocate = MIN2(available, desired_word_size); if (want_to_allocate >= min_word_size) { HeapWord* new_top = obj + want_to_allocate; - HeapWord* result = AtomicAccess::cmpxchg(&_top, obj, new_top); - // result can be one of two: - // the old top value: the exchange succeeded + HeapWord* result = _top.compare_exchange(obj, new_top); + // Result can be one of two: + // the old top value: the exchange succeeded, return. // otherwise: the new value of the top is returned. if (result == obj) { assert(is_object_aligned(obj) && is_object_aligned(new_top), "checking alignment"); *actual_word_size = want_to_allocate; return obj; + } else { + obj = result; } } else { return nullptr; @@ -254,27 +255,27 @@ inline void G1HeapRegion::update_bot_for_block(HeapWord* start, HeapWord* end) { inline HeapWord* G1HeapRegion::parsable_bottom() const { assert(!is_init_completed() || SafepointSynchronize::is_at_safepoint(), "only during initialization or safepoint"); - return _parsable_bottom; + return _parsable_bottom.load_relaxed(); } inline HeapWord* G1HeapRegion::parsable_bottom_acquire() const { - return AtomicAccess::load_acquire(&_parsable_bottom); + return _parsable_bottom.load_acquire(); } inline void G1HeapRegion::reset_parsable_bottom() { - AtomicAccess::release_store(&_parsable_bottom, bottom()); + _parsable_bottom.release_store(bottom()); } inline void G1HeapRegion::note_end_of_marking(HeapWord* top_at_mark_start, size_t marked_bytes, size_t incoming_refs) { assert_at_safepoint(); if (top_at_mark_start != bottom()) { - _garbage_bytes = byte_size(bottom(), top_at_mark_start) - marked_bytes; + _garbage_bytes.store_relaxed(byte_size(bottom(), top_at_mark_start) - marked_bytes); _incoming_refs = incoming_refs; } if (needs_scrubbing()) { - _parsable_bottom = top_at_mark_start; + _parsable_bottom.store_relaxed(top_at_mark_start); } } @@ -286,6 +287,14 @@ inline bool G1HeapRegion::needs_scrubbing() const { return is_old(); } +inline size_t G1HeapRegion::pinned_count() const { + return _pinned_object_count.load_relaxed(); +} + +inline bool G1HeapRegion::has_pinned_objects() const { + return pinned_count() > 0; +} + inline bool G1HeapRegion::in_collection_set() const { return G1CollectedHeap::heap()->is_in_cset(this); } @@ -511,7 +520,7 @@ inline void G1HeapRegion::record_surv_words_in_group(size_t words_survived) { inline void G1HeapRegion::add_pinned_object_count(size_t value) { assert(value != 0, "wasted effort"); assert(!is_free(), "trying to pin free region %u, adding %zu", hrm_index(), value); - AtomicAccess::add(&_pinned_object_count, value, memory_order_relaxed); + _pinned_object_count.add_then_fetch(value, memory_order_relaxed); } inline void G1HeapRegion::install_cset_group(G1CSetCandidateGroup* cset_group) { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp index 795b6543bae..44897c8a277 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.cpp @@ -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 @@ -713,8 +713,10 @@ void G1HeapRegionManager::verify_optional() { G1HeapRegionClaimer::G1HeapRegionClaimer(uint n_workers) : _n_workers(n_workers), _n_regions(G1CollectedHeap::heap()->_hrm._next_highest_used_hrm_index), _claims(nullptr) { - uint* new_claims = NEW_C_HEAP_ARRAY(uint, _n_regions, mtGC); - memset(new_claims, Unclaimed, sizeof(*_claims) * _n_regions); + Atomic* new_claims = NEW_C_HEAP_ARRAY(Atomic, _n_regions, mtGC); + for (uint i = 0; i < _n_regions; i++) { + new_claims[i].store_relaxed(Unclaimed); + } _claims = new_claims; } @@ -730,13 +732,12 @@ uint G1HeapRegionClaimer::offset_for_worker(uint worker_id) const { bool G1HeapRegionClaimer::is_region_claimed(uint region_index) const { assert(region_index < _n_regions, "Invalid index."); - return _claims[region_index] == Claimed; + return _claims[region_index].load_relaxed() == Claimed; } bool G1HeapRegionClaimer::claim_region(uint region_index) { assert(region_index < _n_regions, "Invalid index."); - uint old_val = AtomicAccess::cmpxchg(&_claims[region_index], Unclaimed, Claimed); - return old_val == Unclaimed; + return _claims[region_index].compare_set(Unclaimed, Claimed); } class G1RebuildFreeListTask : public WorkerTask { diff --git a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp index b4ce3b0a8be..eb593ff408e 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionManager.hpp @@ -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 @@ -30,6 +30,7 @@ #include "gc/g1/g1HeapRegionSet.hpp" #include "gc/g1/g1RegionToSpaceMapper.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "services/memoryUsage.hpp" class G1HeapRegion; @@ -294,7 +295,7 @@ public: class G1HeapRegionClaimer : public StackObj { uint _n_workers; uint _n_regions; - volatile uint* _claims; + Atomic* _claims; static const uint Unclaimed = 0; static const uint Claimed = 1; diff --git a/src/hotspot/share/gc/g1/g1YoungCollector.cpp b/src/hotspot/share/gc/g1/g1YoungCollector.cpp index 36cc44a8b7c..a9db9a7c269 100644 --- a/src/hotspot/share/gc/g1/g1YoungCollector.cpp +++ b/src/hotspot/share/gc/g1/g1YoungCollector.cpp @@ -36,6 +36,7 @@ #include "gc/g1/g1EvacFailureRegions.inline.hpp" #include "gc/g1/g1EvacInfo.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" +#include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionPrinter.hpp" #include "gc/g1/g1MonitoringSupport.hpp" #include "gc/g1/g1ParScanThreadState.inline.hpp" diff --git a/src/hotspot/share/gc/g1/vmStructs_g1.hpp b/src/hotspot/share/gc/g1/vmStructs_g1.hpp index 21c86d47a6b..af236ec8581 100644 --- a/src/hotspot/share/gc/g1/vmStructs_g1.hpp +++ b/src/hotspot/share/gc/g1/vmStructs_g1.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1HeapRegion.hpp" #include "gc/g1/g1HeapRegionManager.hpp" +#include "runtime/atomic.hpp" #include "utilities/macros.hpp" #define VM_STRUCTS_G1GC(nonstatic_field, \ @@ -39,9 +40,9 @@ \ nonstatic_field(G1HeapRegion, _type, G1HeapRegionType) \ nonstatic_field(G1HeapRegion, _bottom, HeapWord* const) \ - nonstatic_field(G1HeapRegion, _top, HeapWord* volatile) \ + nonstatic_field(G1HeapRegion, _top, Atomic) \ nonstatic_field(G1HeapRegion, _end, HeapWord* const) \ - volatile_nonstatic_field(G1HeapRegion, _pinned_object_count, size_t) \ + volatile_nonstatic_field(G1HeapRegion, _pinned_object_count, Atomic)\ \ nonstatic_field(G1HeapRegionType, _tag, G1HeapRegionType::Tag volatile) \ \ diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 02572e16728..f65a3441bf4 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -79,6 +79,7 @@ #include "oops/typeArrayOop.hpp" #include "prims/jvmtiAgentThread.hpp" #include "runtime/arguments.hpp" +#include "runtime/atomic.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/jvmFlag.hpp" #include "runtime/globals.hpp" @@ -888,6 +889,7 @@ declare_unsigned_integer_type(unsigned short) \ declare_unsigned_integer_type(jushort) \ declare_unsigned_integer_type(unsigned long) \ + declare_unsigned_integer_type(Atomic) \ /* The compiler thinks this is a different type than */ \ /* unsigned short on Win32 */ \ declare_unsigned_integer_type(u1) \ From 9871e2d3f771ee2bc1b2473c0eb28a0bfc1c5456 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 2 Feb 2026 16:03:04 +0000 Subject: [PATCH 086/215] 8375535: G1: Convert CardTableBarrierSet and subclasses to use Atomic Reviewed-by: kbarrett, iwalulya --- src/hotspot/share/gc/g1/g1BarrierSet.cpp | 22 +++++++++---------- src/hotspot/share/gc/g1/g1BarrierSet.hpp | 7 +++--- .../share/gc/g1/g1BarrierSet.inline.hpp | 6 ++--- .../share/gc/shared/cardTableBarrierSet.cpp | 10 ++++----- .../share/gc/shared/cardTableBarrierSet.hpp | 11 ++++++---- .../gc/shared/cardTableBarrierSet.inline.hpp | 4 ++-- src/hotspot/share/gc/shared/vmStructs_gc.hpp | 4 +++- 7 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.cpp b/src/hotspot/share/gc/g1/g1BarrierSet.cpp index 622651ce0d8..dee50500e07 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.cpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.cpp @@ -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 @@ -64,13 +64,13 @@ G1BarrierSet::G1BarrierSet(G1CardTable* card_table, {} G1BarrierSet::~G1BarrierSet() { - delete _refinement_table; + delete refinement_table(); } void G1BarrierSet::swap_global_card_table() { - G1CardTable* temp = static_cast(_card_table); - _card_table = _refinement_table; - _refinement_table = temp; + G1CardTable* temp = static_cast(card_table()); + _card_table.store_relaxed(refinement_table()); + _refinement_table.store_relaxed(temp); } void G1BarrierSet::update_card_table_base(Thread* thread) { @@ -80,7 +80,7 @@ void G1BarrierSet::update_card_table_base(Thread* thread) { assert(thread->is_Java_thread(), "may only update card table base of JavaThreads, not %s", thread->name()); } #endif - G1ThreadLocalData::set_byte_map_base(thread, _card_table->byte_map_base()); + G1ThreadLocalData::set_byte_map_base(thread, card_table()->byte_map_base()); } template void @@ -135,10 +135,10 @@ void G1BarrierSet::write_region(MemRegion mr) { // marks next time. // If we write to the old card table (after the switching, then the refinement // table) the oncoming handshake will do the memory synchronization. - CardTable* card_table = AtomicAccess::load(&_card_table); + CardTable* local_card_table = card_table(); - volatile CardValue* byte = card_table->byte_for(mr.start()); - CardValue* last_byte = card_table->byte_for(mr.last()); + volatile CardValue* byte = local_card_table->byte_for(mr.start()); + CardValue* last_byte = local_card_table->byte_for(mr.last()); // Dirty cards only if necessary. for (; byte <= last_byte; byte++) { @@ -190,6 +190,6 @@ void G1BarrierSet::on_thread_detach(Thread* thread) { } void G1BarrierSet::print_on(outputStream* st) const { - _card_table->print_on(st, "Card"); - _refinement_table->print_on(st, "Refinement"); + card_table()->print_on(st, "Card"); + refinement_table()->print_on(st, "Refinement"); } diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.hpp index bf595973a32..406096acf10 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.hpp @@ -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 @@ -29,6 +29,7 @@ #include "gc/shared/bufferNode.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" +#include "runtime/atomic.hpp" class G1CardTable; class Thread; @@ -66,7 +67,7 @@ class G1BarrierSet: public CardTableBarrierSet { BufferNode::Allocator _satb_mark_queue_buffer_allocator; G1SATBMarkQueueSet _satb_mark_queue_set; - G1CardTable* _refinement_table; + Atomic _refinement_table; public: G1BarrierSet(G1CardTable* card_table, G1CardTable* refinement_table); @@ -76,7 +77,7 @@ class G1BarrierSet: public CardTableBarrierSet { return barrier_set_cast(BarrierSet::barrier_set()); } - G1CardTable* refinement_table() const { return _refinement_table; } + G1CardTable* refinement_table() const { return _refinement_table.load_relaxed(); } // Swap the global card table references, without synchronization. void swap_global_card_table(); diff --git a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp b/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp index 794e5db0634..54892c9191d 100644 --- a/src/hotspot/share/gc/g1/g1BarrierSet.inline.hpp +++ b/src/hotspot/share/gc/g1/g1BarrierSet.inline.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 @@ -73,8 +73,8 @@ inline void G1BarrierSet::write_ref_field_post(T* field) { // Make sure that the card table reference is read only once. Otherwise the compiler // might reload that value in the two accesses below, that could cause writes to // the wrong card table. - CardTable* card_table = AtomicAccess::load(&_card_table); - CardValue* byte = card_table->byte_for(field); + CardTable* local_card_table = card_table(); + CardValue* byte = local_card_table->byte_for(field); if (*byte == G1CardTable::clean_card_val()) { *byte = G1CardTable::dirty_card_val(); } diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp index 539e40820a8..d6541198858 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.cpp @@ -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 @@ -73,15 +73,15 @@ CardTableBarrierSet::CardTableBarrierSet(CardTable* card_table) : {} CardTableBarrierSet::~CardTableBarrierSet() { - delete _card_table; + delete card_table(); } void CardTableBarrierSet::write_region(MemRegion mr) { - _card_table->dirty_MemRegion(mr); + card_table()->dirty_MemRegion(mr); } void CardTableBarrierSet::print_on(outputStream* st) const { - _card_table->print_on(st); + card_table()->print_on(st); } // Helper for ReduceInitialCardMarks. For performance, @@ -116,7 +116,7 @@ void CardTableBarrierSet::on_slowpath_allocation_exit(JavaThread* thread, oop ne if (!ReduceInitialCardMarks) { return; } - if (new_obj->is_typeArray() || _card_table->is_in_young(new_obj)) { + if (new_obj->is_typeArray() || card_table()->is_in_young(new_obj)) { // Arrays of non-references don't need a post-barrier. } else { MemRegion mr(cast_from_oop(new_obj), new_obj->size()); diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp index c298bfcd0c2..3a9b46d9df8 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.hpp @@ -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 @@ -29,6 +29,7 @@ #include "gc/shared/cardTable.hpp" #include "gc/shared/gc_globals.hpp" #include "memory/memRegion.hpp" +#include "runtime/atomic.hpp" #include "utilities/align.hpp" // This kind of "BarrierSet" allows a "CollectedHeap" to detect and @@ -49,7 +50,7 @@ class CardTableBarrierSet: public BarrierSet { protected: typedef CardTable::CardValue CardValue; - CardTable* _card_table; + Atomic _card_table; CardTableBarrierSet(BarrierSetAssembler* barrier_set_assembler, BarrierSetC1* barrier_set_c1, @@ -90,10 +91,12 @@ public: // at the address "start", which may not necessarily be HeapWord-aligned inline void write_ref_array(HeapWord* start, size_t count); - CardTable* card_table() const { return _card_table; } + CardTable* card_table() { return _card_table.load_relaxed(); } + CardTable* card_table() const { return _card_table.load_relaxed(); } + CardValue* card_table_base_const() const { assert(UseSerialGC || UseParallelGC, "Only these GCs have constant card table base"); - return _card_table->byte_map_base(); + return card_table()->byte_map_base(); } virtual void on_slowpath_allocation_exit(JavaThread* thread, oop new_obj); diff --git a/src/hotspot/share/gc/shared/cardTableBarrierSet.inline.hpp b/src/hotspot/share/gc/shared/cardTableBarrierSet.inline.hpp index ea539a70be5..f60a7f47a19 100644 --- a/src/hotspot/share/gc/shared/cardTableBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shared/cardTableBarrierSet.inline.hpp @@ -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 @@ -35,7 +35,7 @@ template inline void CardTableBarrierSet::write_ref_field_post(T* field) { - volatile CardValue* byte = _card_table->byte_for(field); + volatile CardValue* byte = card_table()->byte_for(field); *byte = CardTable::dirty_card_val(); } diff --git a/src/hotspot/share/gc/shared/vmStructs_gc.hpp b/src/hotspot/share/gc/shared/vmStructs_gc.hpp index 6a29eb25b37..9348fd980f4 100644 --- a/src/hotspot/share/gc/shared/vmStructs_gc.hpp +++ b/src/hotspot/share/gc/shared/vmStructs_gc.hpp @@ -48,6 +48,7 @@ #if INCLUDE_ZGC #include "gc/z/vmStructs_z.hpp" #endif +#include "runtime/atomic.hpp" #define VM_STRUCTS_GC(nonstatic_field, \ volatile_static_field, \ @@ -88,7 +89,7 @@ nonstatic_field(CardTable, _byte_map_size, const size_t) \ nonstatic_field(CardTable, _byte_map, CardTable::CardValue*) \ nonstatic_field(CardTable, _byte_map_base, CardTable::CardValue*) \ - nonstatic_field(CardTableBarrierSet, _card_table, CardTable*) \ + nonstatic_field(CardTableBarrierSet, _card_table, Atomic) \ \ static_field(CollectedHeap, _lab_alignment_reserve, size_t) \ nonstatic_field(CollectedHeap, _reserved, MemRegion) \ @@ -149,6 +150,7 @@ \ declare_toplevel_type(BarrierSet*) \ declare_toplevel_type(CardTable*) \ + declare_toplevel_type(Atomic) \ declare_toplevel_type(CardTable*const) \ declare_toplevel_type(CardTableBarrierSet*) \ declare_toplevel_type(CardTableBarrierSet**) \ From 70f4984a4e1a43fd25169096ee0869361de2b9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galder=20Zamarre=C3=B1o?= Date: Mon, 2 Feb 2026 16:46:46 +0000 Subject: [PATCH 087/215] 8375640: MinMaxIdentity test fails on some machines after 8373134 Reviewed-by: mdoerr, mhaessig, amitkumar --- .../compiler/igvn/TestMinMaxIdentity.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java index d358359ff14..5b998caf65c 100644 --- a/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java +++ b/test/hotspot/jtreg/compiler/igvn/TestMinMaxIdentity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 IBM Corporation. All rights reserved. + * Copyright (c) 2025, 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 @@ -122,8 +122,21 @@ public class TestMinMaxIdentity { let("arg2", arg2), """ @Test - @IR(counts = {IRNode.#op, "= 1"}, - phase = CompilePhase.BEFORE_MACRO_EXPANSION) + """, + type.isFloating() ? + """ + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION, + applyIfPlatform = {"riscv64", "true"}) + """ : + """ + @IR(counts = {IRNode.#op, "= 1"}, + phase = CompilePhase.BEFORE_MACRO_EXPANSION) + """, + """ @Arguments(values = {Argument.NUMBER_42, Argument.NUMBER_42}) public #type $test(#type #arg1, #type #arg2) { int i; From b60249882cc511a7fc9cf9ae11e8beb1602ea10f Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 2 Feb 2026 16:57:47 +0000 Subject: [PATCH 088/215] 8376126: G1: Convert remaining volatiles in G1ConcurrentMark to Atomic Reviewed-by: iwalulya, kbarrett, stefank --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 70 +++++++++++-------- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 32 ++++----- .../share/gc/g1/g1ConcurrentMark.inline.hpp | 14 ++-- .../share/gc/g1/g1RegionMarkStatsCache.hpp | 2 + 4 files changed, 64 insertions(+), 54 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 5f096c2b9d7..4ed0a3065bc 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -24,6 +24,7 @@ #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderDataGraph.hpp" +#include "cppstdlib/new.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BatchedTask.hpp" #include "gc/g1/g1CardSetMemory.hpp" @@ -519,8 +520,8 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _max_concurrent_workers(0), _region_mark_stats(NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_num_regions(), mtGC)), - _top_at_mark_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)), - _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)), + _top_at_mark_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)), + _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)), _needs_remembered_set_rebuild(false) { assert(G1CGC_lock != nullptr, "CGC_lock must be initialized"); @@ -564,6 +565,12 @@ void G1ConcurrentMark::fully_initialize() { _tasks[i] = new G1CMTask(i, this, task_queue, _region_mark_stats); } + for (uint i = 0; i < _g1h->max_num_regions(); i++) { + ::new (&_region_mark_stats[i]) G1RegionMarkStats{}; + ::new (&_top_at_mark_starts[i]) Atomic{}; + ::new (&_top_at_rebuild_starts[i]) Atomic{}; + } + reset_at_marking_complete(); } @@ -576,7 +583,7 @@ PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const } void G1ConcurrentMark::reset() { - _has_aborted = false; + _has_aborted.store_relaxed(false); reset_marking_for_restart(); @@ -588,7 +595,7 @@ void G1ConcurrentMark::reset() { uint max_num_regions = _g1h->max_num_regions(); for (uint i = 0; i < max_num_regions; i++) { - _top_at_rebuild_starts[i] = nullptr; + _top_at_rebuild_starts[i].store_relaxed(nullptr); _region_mark_stats[i].clear(); } @@ -600,7 +607,7 @@ void G1ConcurrentMark::clear_statistics(G1HeapRegion* r) { for (uint j = 0; j < _max_num_tasks; ++j) { _tasks[j]->clear_mark_stats_cache(region_idx); } - _top_at_rebuild_starts[region_idx] = nullptr; + _top_at_rebuild_starts[region_idx].store_relaxed(nullptr); _region_mark_stats[region_idx].clear(); } @@ -636,7 +643,7 @@ void G1ConcurrentMark::reset_marking_for_restart() { } clear_has_overflown(); - _finger = _heap.start(); + _finger.store_relaxed(_heap.start()); for (uint i = 0; i < _max_num_tasks; ++i) { G1CMTaskQueue* queue = _task_queues->queue(i); @@ -658,14 +665,14 @@ void G1ConcurrentMark::set_concurrency(uint active_tasks) { void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurrent) { set_concurrency(active_tasks); - _concurrent = concurrent; + _concurrent.store_relaxed(concurrent); if (!concurrent) { // At this point we should be in a STW phase, and completed marking. assert_at_safepoint_on_vm_thread(); assert(out_of_regions(), "only way to get here: _finger: " PTR_FORMAT ", _heap_end: " PTR_FORMAT, - p2i(_finger), p2i(_heap.end())); + p2i(finger()), p2i(_heap.end())); } } @@ -696,8 +703,8 @@ void G1ConcurrentMark::reset_at_marking_complete() { } G1ConcurrentMark::~G1ConcurrentMark() { - FREE_C_HEAP_ARRAY(HeapWord*, _top_at_mark_starts); - FREE_C_HEAP_ARRAY(HeapWord*, _top_at_rebuild_starts); + FREE_C_HEAP_ARRAY(Atomic, _top_at_mark_starts); + FREE_C_HEAP_ARRAY(Atomic, _top_at_rebuild_starts); FREE_C_HEAP_ARRAY(G1RegionMarkStats, _region_mark_stats); // The G1ConcurrentMark instance is never freed. ShouldNotReachHere(); @@ -1164,7 +1171,7 @@ void G1ConcurrentMark::concurrent_cycle_start() { } uint G1ConcurrentMark::completed_mark_cycles() const { - return AtomicAccess::load(&_completed_mark_cycles); + return _completed_mark_cycles.load_relaxed(); } void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) { @@ -1173,7 +1180,7 @@ void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) { _g1h->trace_heap_after_gc(_gc_tracer_cm); if (mark_cycle_completed) { - AtomicAccess::inc(&_completed_mark_cycles, memory_order_relaxed); + _completed_mark_cycles.add_then_fetch(1u, memory_order_relaxed); } if (has_aborted()) { @@ -1187,7 +1194,7 @@ void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) { } void G1ConcurrentMark::mark_from_roots() { - _restart_for_overflow = false; + _restart_for_overflow.store_relaxed(false); uint active_workers = calc_active_marking_workers(); @@ -1356,7 +1363,7 @@ void G1ConcurrentMark::remark() { } } else { // We overflowed. Restart concurrent marking. - _restart_for_overflow = true; + _restart_for_overflow.store_relaxed(true); verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyLocation::RemarkOverflow); @@ -1785,44 +1792,45 @@ void G1ConcurrentMark::clear_bitmap_for_region(G1HeapRegion* hr) { } G1HeapRegion* G1ConcurrentMark::claim_region(uint worker_id) { - // "checkpoint" the finger - HeapWord* finger = _finger; + // "Checkpoint" the finger. + HeapWord* local_finger = finger(); - while (finger < _heap.end()) { - assert(_g1h->is_in_reserved(finger), "invariant"); + while (local_finger < _heap.end()) { + assert(_g1h->is_in_reserved(local_finger), "invariant"); - G1HeapRegion* curr_region = _g1h->heap_region_containing_or_null(finger); + G1HeapRegion* curr_region = _g1h->heap_region_containing_or_null(local_finger); // Make sure that the reads below do not float before loading curr_region. OrderAccess::loadload(); // Above heap_region_containing may return null as we always scan claim // until the end of the heap. In this case, just jump to the next region. - HeapWord* end = curr_region != nullptr ? curr_region->end() : finger + G1HeapRegion::GrainWords; + HeapWord* end = curr_region != nullptr ? curr_region->end() : local_finger + G1HeapRegion::GrainWords; // Is the gap between reading the finger and doing the CAS too long? - HeapWord* res = AtomicAccess::cmpxchg(&_finger, finger, end); - if (res == finger && curr_region != nullptr) { - // we succeeded + HeapWord* res = _finger.compare_exchange(local_finger, end); + if (res == local_finger && curr_region != nullptr) { + // We succeeded. HeapWord* bottom = curr_region->bottom(); HeapWord* limit = top_at_mark_start(curr_region); log_trace(gc, marking)("Claim region %u bottom " PTR_FORMAT " tams " PTR_FORMAT, curr_region->hrm_index(), p2i(curr_region->bottom()), p2i(top_at_mark_start(curr_region))); - // notice that _finger == end cannot be guaranteed here since, - // someone else might have moved the finger even further - assert(_finger >= end, "the finger should have moved forward"); + // Notice that _finger == end cannot be guaranteed here since, + // someone else might have moved the finger even further. + assert(finger() >= end, "The finger should have moved forward"); if (limit > bottom) { return curr_region; } else { assert(limit == bottom, - "the region limit should be at bottom"); + "The region limit should be at bottom"); // We return null and the caller should try calling // claim_region() again. return nullptr; } } else { - assert(_finger > finger, "the finger should have moved forward"); - // read it again - finger = _finger; + // Read the finger again. + HeapWord* next_finger = finger(); + assert(next_finger > local_finger, "The finger should have moved forward " PTR_FORMAT " " PTR_FORMAT, p2i(local_finger), p2i(next_finger)); + local_finger = next_finger; } } @@ -1962,7 +1970,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { void G1ConcurrentMark::abort_marking_threads() { assert(!_root_regions.scan_in_progress(), "still doing root region scan"); - _has_aborted = true; + _has_aborted.store_relaxed(true); _first_overflow_barrier_sync.abort(); _second_overflow_barrier_sync.abort(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 3a4cbf1b83e..39d98db9876 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -368,7 +368,7 @@ class G1ConcurrentMark : public CHeapObj { // For grey objects G1CMMarkStack _global_mark_stack; // Grey objects behind global finger - HeapWord* volatile _finger; // The global finger, region aligned, + Atomic _finger; // The global finger, region aligned, // always pointing to the end of the // last claimed region @@ -395,19 +395,19 @@ class G1ConcurrentMark : public CHeapObj { WorkerThreadsBarrierSync _second_overflow_barrier_sync; // Number of completed mark cycles. - volatile uint _completed_mark_cycles; + Atomic _completed_mark_cycles; // This is set by any task, when an overflow on the global data // structures is detected - volatile bool _has_overflown; + Atomic _has_overflown; // True: marking is concurrent, false: we're in remark - volatile bool _concurrent; + Atomic _concurrent; // Set at the end of a Full GC so that marking aborts - volatile bool _has_aborted; + Atomic _has_aborted; // Used when remark aborts due to an overflow to indicate that // another concurrent marking phase should start - volatile bool _restart_for_overflow; + Atomic _restart_for_overflow; ConcurrentGCTimer* _gc_timer_cm; @@ -461,8 +461,8 @@ class G1ConcurrentMark : public CHeapObj { void print_and_reset_taskqueue_stats(); - HeapWord* finger() { return _finger; } - bool concurrent() { return _concurrent; } + HeapWord* finger() { return _finger.load_relaxed(); } + bool concurrent() { return _concurrent.load_relaxed(); } uint active_tasks() { return _num_active_tasks; } TaskTerminator* terminator() { return &_terminator; } @@ -487,7 +487,7 @@ class G1ConcurrentMark : public CHeapObj { // to satisfy an allocation without doing a GC. This is fine, because all // objects in those regions will be considered live anyway because of // SATB guarantees (i.e. their TAMS will be equal to bottom). - bool out_of_regions() { return _finger >= _heap.end(); } + bool out_of_regions() { return finger() >= _heap.end(); } // Returns the task with the given id G1CMTask* task(uint id) { @@ -499,10 +499,10 @@ class G1ConcurrentMark : public CHeapObj { // Access / manipulation of the overflow flag which is set to // indicate that the global stack has overflown - bool has_overflown() { return _has_overflown; } - void set_has_overflown() { _has_overflown = true; } - void clear_has_overflown() { _has_overflown = false; } - bool restart_for_overflow() { return _restart_for_overflow; } + bool has_overflown() { return _has_overflown.load_relaxed(); } + void set_has_overflown() { _has_overflown.store_relaxed(true); } + void clear_has_overflown() { _has_overflown.store_relaxed(false); } + bool restart_for_overflow() { return _restart_for_overflow.load_relaxed(); } // Methods to enter the two overflow sync barriers void enter_first_sync_barrier(uint worker_id); @@ -516,12 +516,12 @@ class G1ConcurrentMark : public CHeapObj { G1RegionMarkStats* _region_mark_stats; // Top pointer for each region at the start of marking. Must be valid for all committed // regions. - HeapWord* volatile* _top_at_mark_starts; + Atomic* _top_at_mark_starts; // Top pointer for each region at the start of the rebuild remembered set process // for regions which remembered sets need to be rebuilt. A null for a given region // means that this region does not be scanned during the rebuilding remembered // set phase at all. - HeapWord* volatile* _top_at_rebuild_starts; + Atomic* _top_at_rebuild_starts; // True when Remark pause selected regions for rebuilding. bool _needs_remembered_set_rebuild; public: @@ -679,7 +679,7 @@ public: uint completed_mark_cycles() const; - bool has_aborted() { return _has_aborted; } + bool has_aborted() { return _has_aborted.load_relaxed(); } void print_summary_info(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp index 2f4824e4cae..21167d5cae9 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp @@ -194,11 +194,11 @@ inline void G1CMTask::process_array_chunk(objArrayOop obj, size_t start, size_t inline void G1ConcurrentMark::update_top_at_mark_start(G1HeapRegion* r) { uint const region = r->hrm_index(); assert(region < _g1h->max_num_regions(), "Tried to access TAMS for region %u out of bounds", region); - _top_at_mark_starts[region] = r->top(); + _top_at_mark_starts[region].store_relaxed(r->top()); } inline void G1ConcurrentMark::reset_top_at_mark_start(G1HeapRegion* r) { - _top_at_mark_starts[r->hrm_index()] = r->bottom(); + _top_at_mark_starts[r->hrm_index()].store_relaxed(r->bottom()); } inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) const { @@ -207,7 +207,7 @@ inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) cons inline HeapWord* G1ConcurrentMark::top_at_mark_start(uint region) const { assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region); - return _top_at_mark_starts[region]; + return _top_at_mark_starts[region].load_relaxed(); } inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const { @@ -217,7 +217,7 @@ inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const { } inline HeapWord* G1ConcurrentMark::top_at_rebuild_start(G1HeapRegion* r) const { - return _top_at_rebuild_starts[r->hrm_index()]; + return _top_at_rebuild_starts[r->hrm_index()].load_relaxed(); } inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) { @@ -225,10 +225,10 @@ inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) { uint const region = r->hrm_index(); assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region); - assert(_top_at_rebuild_starts[region] == nullptr, + assert(top_at_rebuild_start(r) == nullptr, "TARS for region %u has already been set to " PTR_FORMAT " should be null", - region, p2i(_top_at_rebuild_starts[region])); - _top_at_rebuild_starts[region] = r->top(); + region, p2i(top_at_rebuild_start(r))); + _top_at_rebuild_starts[region].store_relaxed(r->top()); } inline void G1CMTask::update_liveness(oop const obj, const size_t obj_size) { diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp index 4dcdd33846e..b8f13f4553d 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp @@ -44,6 +44,8 @@ struct G1RegionMarkStats { Atomic _live_words; Atomic _incoming_refs; + G1RegionMarkStats() : _live_words(0), _incoming_refs(0) { } + // Clear all members. void clear() { _live_words.store_relaxed(0); From 8023c41690aee648eef800b69e517136e1cd062c Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Mon, 2 Feb 2026 18:49:45 +0000 Subject: [PATCH 089/215] 8376703: Some coding in libjimage seems to be not called at all or not called from PRODUCT code Reviewed-by: alanb, rriggs --- .../share/native/libjimage/endian.cpp | 5 +- .../share/native/libjimage/endian.hpp | 5 +- .../native/libjimage/imageDecompressor.hpp | 1 - .../share/native/libjimage/imageFile.cpp | 48 +------------------ .../share/native/libjimage/imageFile.hpp | 18 +------ 5 files changed, 4 insertions(+), 73 deletions(-) diff --git a/src/java.base/share/native/libjimage/endian.cpp b/src/java.base/share/native/libjimage/endian.cpp index 5e13ffba34e..73d8aefe086 100644 --- a/src/java.base/share/native/libjimage/endian.cpp +++ b/src/java.base/share/native/libjimage/endian.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -104,6 +104,3 @@ void Endian::set_java(u1* p, u2 x) { p[1] = x & 0xff; } -Endian* Endian::get_native_handler() { - return NativeEndian::get_native(); -} diff --git a/src/java.base/share/native/libjimage/endian.hpp b/src/java.base/share/native/libjimage/endian.hpp index 42fbbb0e853..38e566b7524 100644 --- a/src/java.base/share/native/libjimage/endian.hpp +++ b/src/java.base/share/native/libjimage/endian.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -74,9 +74,6 @@ public: // Select an appropriate endian handler. static Endian* get_handler(bool big_endian); - // Return the native endian handler. - static Endian* get_native_handler(); - // get platform u2 from Java Big endian static u2 get_java(u1* x); // set platform u2 to Java Big endian diff --git a/src/java.base/share/native/libjimage/imageDecompressor.hpp b/src/java.base/share/native/libjimage/imageDecompressor.hpp index 057fa15917c..709e1a3bb21 100644 --- a/src/java.base/share/native/libjimage/imageDecompressor.hpp +++ b/src/java.base/share/native/libjimage/imageDecompressor.hpp @@ -111,7 +111,6 @@ protected: public: static void image_decompressor_init(); - static void image_decompressor_close(); static ImageDecompressor* get_decompressor(const char * decompressor_name) ; static void decompress_resource(u1* compressed, u1* uncompressed, u8 uncompressed_size, const ImageStrings* strings, Endian* _endian); diff --git a/src/java.base/share/native/libjimage/imageFile.cpp b/src/java.base/share/native/libjimage/imageFile.cpp index d97a8f95a60..e2479ba2c9e 100644 --- a/src/java.base/share/native/libjimage/imageFile.cpp +++ b/src/java.base/share/native/libjimage/imageFile.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. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -180,16 +180,6 @@ void ImageFileReaderTable::remove(ImageFileReader* image) { } } -// Determine if image entry is in table. -bool ImageFileReaderTable::contains(ImageFileReader* image) { - for (u4 i = 0; i < _count; i++) { - if (_table[i] == image) { - return true; - } - } - return false; -} - // Table to manage multiple opens of an image file. ImageFileReaderTable ImageFileReader::_reader_table; @@ -261,25 +251,6 @@ void ImageFileReader::close(ImageFileReader *reader) { } } -// Return an id for the specified ImageFileReader. -u8 ImageFileReader::reader_to_ID(ImageFileReader *reader) { - // ID is just the cloaked reader address. - return (u8)reader; -} - -// Validate the image id. -bool ImageFileReader::id_check(u8 id) { - // Make sure the ID is a managed (_reader_table) reader. - SimpleCriticalSectionLock cs(&_reader_table_lock); - return _reader_table.contains((ImageFileReader*)id); -} - -// Return an id for the specified ImageFileReader. -ImageFileReader* ImageFileReader::id_to_reader(u8 id) { - assert(id_check(id) && "invalid image id"); - return (ImageFileReader*)id; -} - // Constructor initializes to a closed state. ImageFileReader::ImageFileReader(const char* name, bool big_endian) { // Copy the image file name. @@ -372,23 +343,6 @@ bool ImageFileReader::read_at(u1* data, u8 size, u8 offset) const { return (u8)osSupport::read(_fd, (char*)data, size, offset) == size; } -// Find the location attributes associated with the path. Returns true if -// the location is found, false otherwise. -bool ImageFileReader::find_location(const char* path, ImageLocation& location) const { - // Locate the entry in the index perfect hash table. - s4 index = ImageStrings::find(_endian, path, _redirect_table, table_length()); - // If is found. - if (index != ImageStrings::NOT_FOUND) { - // Get address of first byte of location attribute stream. - u1* data = get_location_data(index); - // Expand location attributes. - location.set_data(data); - // Make sure result is not a false positive. - return verify_location(location, path); - } - return false; -} - // Find the location index and size associated with the path. // Returns the location index and size if the location is found, 0 otherwise. u4 ImageFileReader::find_location_index(const char* path, u8 *size) const { diff --git a/src/java.base/share/native/libjimage/imageFile.hpp b/src/java.base/share/native/libjimage/imageFile.hpp index 5fb4ea3baaa..a4c8d159efa 100644 --- a/src/java.base/share/native/libjimage/imageFile.hpp +++ b/src/java.base/share/native/libjimage/imageFile.hpp @@ -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. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -375,9 +375,6 @@ public: // Remove an image entry from the table. void remove(ImageFileReader* image); - - // Determine if image entry is in table. - bool contains(ImageFileReader* image); }; // Manage the image file. @@ -445,15 +442,6 @@ public: // Close an image file if the file is not in use elsewhere. static void close(ImageFileReader *reader); - // Return an id for the specified ImageFileReader. - static u8 reader_to_ID(ImageFileReader *reader); - - // Validate the image id. - static bool id_check(u8 id); - - // Return an id for the specified ImageFileReader. - static ImageFileReader* id_to_reader(u8 id); - // Open image file for read access. bool open(); @@ -545,10 +533,6 @@ public: return _endian->get(_offsets_table[index]); } - // Find the location attributes associated with the path. Returns true if - // the location is found, false otherwise. - bool find_location(const char* path, ImageLocation& location) const; - // Find the location index and size associated with the path. // Returns the location index and size if the location is found, // ImageFileReader::NOT_FOUND otherwise. From 5607a4620c97ad2650a2dd3f464d03955fe17ef1 Mon Sep 17 00:00:00 2001 From: Hendrik Schick Date: Mon, 2 Feb 2026 20:58:03 +0000 Subject: [PATCH 090/215] 8376954: Typos in CharacterRangeInfo and AsynchronousServerSocketChannel Reviewed-by: liach, bpb --- .../java/lang/classfile/attribute/CharacterRangeInfo.java | 6 +++--- .../java/nio/channels/AsynchronousServerSocketChannel.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java b/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java index f6f5f9928bc..e1cc52bfa1d 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/CharacterRangeInfo.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 @@ -124,7 +124,7 @@ public sealed interface CharacterRangeInfo *
  • {@link CharacterRange#FLAG_BRANCH_TRUE} A condition encoded * in the branch instruction immediately contained in the code range for * this item is not inverted towards the corresponding branch condition in - * the source code. I.e. actual jump occurs if and only if the the source + * the source code. I.e. actual jump occurs if and only if the source * code branch condition evaluates to true. Entries of this type are * produced only for conditions that are listed in the description of * CRT_FLOW_CONTROLLER flag. The source range for the entry contains flow @@ -136,7 +136,7 @@ public sealed interface CharacterRangeInfo *
  • {@link CharacterRange#FLAG_BRANCH_FALSE} A condition encoded * in the branch instruction immediately contained in the code range for * this item is inverted towards the corresponding branch condition in the - * source code. I.e. actual jump occurs if and only if the the source code + * source code. I.e. actual jump occurs if and only if the source code * branch condition evaluates to false. Entries of this type are produced * only for conditions that are listed in the description of * CRT_FLOW_CONTROLLER flag. The source range for the entry contains flow diff --git a/src/java.base/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java b/src/java.base/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java index 192b1f7958b..4e6cadc737c 100644 --- a/src/java.base/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java +++ b/src/java.base/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2024, 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 @@ -208,7 +208,7 @@ public abstract class AsynchronousServerSocketChannel *

    The {@code backlog} parameter is the maximum number of pending * connections on the socket. Its exact semantics are implementation specific. * In particular, an implementation may impose a maximum length or may choose - * to ignore the parameter altogther. If the {@code backlog} parameter has + * to ignore the parameter altogether. If the {@code backlog} parameter has * the value {@code 0}, or a negative value, then an implementation specific * default is used. * From 4db0f7f29154d6618c63a30ef2a86267c842ebb3 Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Mon, 2 Feb 2026 21:53:02 +0000 Subject: [PATCH 091/215] 8375057: Update HarfBuzz to 12.3.2 Reviewed-by: prr, kizune --- src/java.desktop/share/legal/harfbuzz.md | 2 +- .../native/libharfbuzz/OT/Color/COLR/COLR.hh | 92 +- .../native/libharfbuzz/OT/Color/CPAL/CPAL.hh | 1 + .../libharfbuzz/OT/Layout/Common/Coverage.hh | 58 +- .../OT/Layout/Common/CoverageFormat1.hh | 4 +- .../OT/Layout/Common/CoverageFormat2.hh | 2 +- .../native/libharfbuzz/OT/Layout/GDEF/GDEF.hh | 58 +- .../libharfbuzz/OT/Layout/GPOS/Anchor.hh | 14 +- .../OT/Layout/GPOS/AnchorFormat3.hh | 15 +- .../OT/Layout/GPOS/AnchorMatrix.hh | 7 + .../libharfbuzz/OT/Layout/GPOS/CursivePos.hh | 8 +- .../OT/Layout/GPOS/CursivePosFormat1.hh | 15 +- .../native/libharfbuzz/OT/Layout/GPOS/GPOS.hh | 66 +- .../OT/Layout/GPOS/LigatureArray.hh | 19 +- .../libharfbuzz/OT/Layout/GPOS/MarkArray.hh | 10 +- .../libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh | 8 +- .../OT/Layout/GPOS/MarkBasePosFormat1.hh | 33 +- .../libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh | 8 +- .../OT/Layout/GPOS/MarkLigPosFormat1.hh | 18 +- .../libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh | 8 +- .../OT/Layout/GPOS/MarkMarkPosFormat1.hh | 32 +- .../libharfbuzz/OT/Layout/GPOS/PairPos.hh | 8 +- .../OT/Layout/GPOS/PairPosFormat1.hh | 41 +- .../OT/Layout/GPOS/PairPosFormat2.hh | 52 +- .../libharfbuzz/OT/Layout/GPOS/SinglePos.hh | 14 +- .../libharfbuzz/OT/Layout/GPOS/ValueFormat.hh | 13 +- .../OT/Layout/GSUB/AlternateSet.hh | 13 + .../OT/Layout/GSUB/AlternateSubst.hh | 14 +- .../OT/Layout/GSUB/AlternateSubstFormat1.hh | 13 + .../libharfbuzz/OT/Layout/GSUB/Ligature.hh | 31 +- .../libharfbuzz/OT/Layout/GSUB/LigatureSet.hh | 30 +- .../OT/Layout/GSUB/LigatureSubst.hh | 14 +- .../OT/Layout/GSUB/LigatureSubstFormat1.hh | 52 +- .../OT/Layout/GSUB/MultipleSubst.hh | 14 +- .../OT/Layout/GSUB/ReverseChainSingleSubst.hh | 8 +- .../libharfbuzz/OT/Layout/GSUB/Sequence.hh | 2 +- .../libharfbuzz/OT/Layout/GSUB/SingleSubst.hh | 14 +- .../OT/Layout/GSUB/SingleSubstFormat1.hh | 15 + .../OT/Layout/GSUB/SingleSubstFormat2.hh | 11 + .../native/libharfbuzz/OT/Layout/types.hh | 7 +- .../native/libharfbuzz/OT/Var/VARC/VARC.cc | 421 + .../native/libharfbuzz/OT/Var/VARC/VARC.hh | 22 +- .../share/native/libharfbuzz/OT/glyf/Glyph.hh | 38 +- .../native/libharfbuzz/OT/glyf/SimpleGlyph.hh | 2 +- .../share/native/libharfbuzz/OT/glyf/glyf.hh | 149 +- .../libharfbuzz/graph/classdef-graph.hh | 4 +- .../libharfbuzz/graph/coverage-graph.hh | 72 +- .../share/native/libharfbuzz/graph/graph.hh | 336 +- .../libharfbuzz/graph/gsubgpos-context.hh | 1 + .../libharfbuzz/graph/gsubgpos-graph.hh | 116 +- .../libharfbuzz/graph/markbasepos-graph.hh | 12 +- .../native/libharfbuzz/graph/pairpos-graph.hh | 19 +- .../native/libharfbuzz/graph/serialize.hh | 45 +- .../native/libharfbuzz/graph/split-helpers.hh | 4 +- .../libharfbuzz/hb-aat-layout-common.hh | 134 +- .../libharfbuzz/hb-aat-layout-kerx-table.hh | 74 +- .../libharfbuzz/hb-aat-layout-morx-table.hh | 23 +- .../share/native/libharfbuzz/hb-algs.hh | 288 +- .../share/native/libharfbuzz/hb-alloc-pool.hh | 105 + .../share/native/libharfbuzz/hb-array.hh | 7 + .../share/native/libharfbuzz/hb-atomic.hh | 96 +- .../share/native/libharfbuzz/hb-bimap.hh | 2 +- .../share/native/libharfbuzz/hb-bit-page.hh | 33 +- .../libharfbuzz/hb-bit-set-invertible.hh | 2 +- .../share/native/libharfbuzz/hb-bit-set.hh | 13 +- .../hb-buffer-deserialize-text-unicode.hh | 10 +- .../native/libharfbuzz/hb-buffer-serialize.cc | 2 +- .../native/libharfbuzz/hb-buffer-verify.cc | 6 +- .../share/native/libharfbuzz/hb-buffer.cc | 36 +- .../share/native/libharfbuzz/hb-buffer.hh | 73 +- .../share/native/libharfbuzz/hb-cache.hh | 33 +- .../native/libharfbuzz/hb-cff2-interp-cs.hh | 4 +- .../share/native/libharfbuzz/hb-common.cc | 47 +- .../share/native/libharfbuzz/hb-config.hh | 16 +- .../share/native/libharfbuzz/hb-debug.hh | 48 +- .../share/native/libharfbuzz/hb-deprecated.h | 8 +- .../share/native/libharfbuzz/hb-draw.cc | 20 +- .../native/libharfbuzz/hb-face-builder.cc | 3 +- .../share/native/libharfbuzz/hb-face.cc | 5 +- .../share/native/libharfbuzz/hb-font.cc | 248 +- .../share/native/libharfbuzz/hb-font.h | 129 +- .../share/native/libharfbuzz/hb-font.hh | 365 +- .../{hb-pool.hh => hb-free-pool.hh} | 14 +- .../share/native/libharfbuzz/hb-ft.cc | 30 +- .../share/native/libharfbuzz/hb-geometry.hh | 249 +- .../share/native/libharfbuzz/hb-iter.hh | 11 +- .../share/native/libharfbuzz/hb-kern.hh | 2 +- .../share/native/libharfbuzz/hb-limits.hh | 8 +- .../share/native/libharfbuzz/hb-machinery.hh | 24 +- .../share/native/libharfbuzz/hb-map.hh | 51 +- .../native/libharfbuzz/hb-number-parser.hh | 8 +- .../share/native/libharfbuzz/hb-open-file.hh | 14 +- .../share/native/libharfbuzz/hb-open-type.hh | 203 +- .../native/libharfbuzz/hb-ot-cff-common.hh | 2 +- .../native/libharfbuzz/hb-ot-cff1-std-str.hh | 782 +- .../native/libharfbuzz/hb-ot-cff1-table.hh | 4 +- .../native/libharfbuzz/hb-ot-cff2-table.cc | 6 +- .../native/libharfbuzz/hb-ot-cmap-table.hh | 53 +- .../share/native/libharfbuzz/hb-ot-font.cc | 578 +- .../native/libharfbuzz/hb-ot-hmtx-table.hh | 79 +- .../native/libharfbuzz/hb-ot-kern-table.hh | 20 +- .../libharfbuzz/hb-ot-layout-base-table.hh | 20 +- .../native/libharfbuzz/hb-ot-layout-common.hh | 746 +- .../libharfbuzz/hb-ot-layout-gpos-table.hh | 10 +- .../libharfbuzz/hb-ot-layout-gsub-table.hh | 10 +- .../libharfbuzz/hb-ot-layout-gsubgpos.hh | 1174 ++- .../share/native/libharfbuzz/hb-ot-layout.cc | 162 +- .../share/native/libharfbuzz/hb-ot-layout.h | 6 + .../share/native/libharfbuzz/hb-ot-layout.hh | 32 +- .../native/libharfbuzz/hb-ot-math-table.hh | 6 +- .../native/libharfbuzz/hb-ot-post-macroman.hh | 516 +- .../libharfbuzz/hb-ot-shape-fallback.cc | 29 +- .../libharfbuzz/hb-ot-shape-normalize.cc | 12 +- .../libharfbuzz/hb-ot-shape-normalize.hh | 2 +- .../share/native/libharfbuzz/hb-ot-shape.cc | 216 +- .../share/native/libharfbuzz/hb-ot-shape.hh | 2 +- .../hb-ot-shaper-arabic-joining-list.hh | 8 +- .../libharfbuzz/hb-ot-shaper-arabic-table.hh | 22 +- .../native/libharfbuzz/hb-ot-shaper-arabic.cc | 2 +- .../native/libharfbuzz/hb-ot-shaper-hangul.cc | 2 +- .../libharfbuzz/hb-ot-shaper-indic-machine.hh | 14 +- .../libharfbuzz/hb-ot-shaper-indic-table.cc | 12 +- .../native/libharfbuzz/hb-ot-shaper-indic.cc | 48 +- .../libharfbuzz/hb-ot-shaper-khmer-machine.hh | 14 +- .../native/libharfbuzz/hb-ot-shaper-khmer.cc | 6 - .../hb-ot-shaper-myanmar-machine.hh | 14 +- .../native/libharfbuzz/hb-ot-shaper-thai.cc | 6 +- .../libharfbuzz/hb-ot-shaper-use-machine.hh | 14 +- .../libharfbuzz/hb-ot-shaper-use-table.hh | 727 +- .../hb-ot-shaper-vowel-constraints.cc | 4 +- .../share/native/libharfbuzz/hb-ot-shaper.hh | 6 + .../native/libharfbuzz/hb-ot-stat-table.hh | 20 +- .../native/libharfbuzz/hb-ot-tag-table.hh | 8 +- .../libharfbuzz/hb-ot-var-avar-table.hh | 178 +- .../native/libharfbuzz/hb-ot-var-common.hh | 717 +- .../libharfbuzz/hb-ot-var-cvar-table.hh | 7 +- .../libharfbuzz/hb-ot-var-fvar-table.hh | 25 +- .../libharfbuzz/hb-ot-var-gvar-table.hh | 165 +- .../libharfbuzz/hb-ot-var-hvar-table.hh | 101 +- .../share/native/libharfbuzz/hb-ot-var.cc | 20 +- .../native/libharfbuzz/hb-ot-vorg-table.hh | 1 + .../share/native/libharfbuzz/hb-outline.cc | 9 + .../share/native/libharfbuzz/hb-outline.hh | 1 + .../native/libharfbuzz/hb-paint-extents.cc | 14 +- .../native/libharfbuzz/hb-paint-extents.hh | 38 +- .../share/native/libharfbuzz/hb-paint.hh | 76 +- .../native/libharfbuzz/hb-priority-queue.hh | 12 +- .../share/native/libharfbuzz/hb-repacker.hh | 27 +- .../share/native/libharfbuzz/hb-sanitize.hh | 98 +- .../share/native/libharfbuzz/hb-script-list.h | 12 + .../share/native/libharfbuzz/hb-serialize.hh | 9 +- .../share/native/libharfbuzz/hb-set-digest.hh | 6 +- .../share/native/libharfbuzz/hb-shape.cc | 8 +- .../native/libharfbuzz/hb-shaper-list.hh | 8 + .../share/native/libharfbuzz/hb-static.cc | 23 - .../native/libharfbuzz/hb-string-array.hh | 12 +- .../libharfbuzz/hb-subset-cff-common.hh | 6 +- .../libharfbuzz/hb-subset-instancer-iup.hh | 15 + .../libharfbuzz/hb-subset-instancer-solver.cc | 38 +- .../libharfbuzz/hb-subset-instancer-solver.hh | 22 +- .../libharfbuzz/hb-subset-plan-member-list.hh | 4 + .../native/libharfbuzz/hb-subset-plan.cc | 19 +- .../native/libharfbuzz/hb-subset-plan.hh | 3 +- .../share/native/libharfbuzz/hb-subset.cc | 584 +- .../share/native/libharfbuzz/hb-subset.h | 5 + .../share/native/libharfbuzz/hb-subset.hh | 1 - .../share/native/libharfbuzz/hb-ucd-table.hh | 9165 ++++++++--------- .../libharfbuzz/hb-unicode-emoji-table.hh | 86 +- .../share/native/libharfbuzz/hb-unicode.hh | 51 + .../share/native/libharfbuzz/hb-utf.hh | 20 +- .../share/native/libharfbuzz/hb-vector.hh | 187 +- .../share/native/libharfbuzz/hb-version.h | 8 +- .../share/native/libharfbuzz/hb.hh | 29 +- 173 files changed, 11918 insertions(+), 9815 deletions(-) create mode 100644 src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.cc create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-alloc-pool.hh rename src/java.desktop/share/native/libharfbuzz/{hb-pool.hh => hb-free-pool.hh} (92%) diff --git a/src/java.desktop/share/legal/harfbuzz.md b/src/java.desktop/share/legal/harfbuzz.md index e0c40705ed6..3aa5ca841d6 100644 --- a/src/java.desktop/share/legal/harfbuzz.md +++ b/src/java.desktop/share/legal/harfbuzz.md @@ -1,4 +1,4 @@ -## Harfbuzz 11.2.0 +## Harfbuzz 12.3.2 ### Harfbuzz License diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh index 73e6b4c305b..011b5ad5520 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/COLR/COLR.hh @@ -104,7 +104,7 @@ public: foreground (foreground_), instancer (instancer_) { - if (font->is_synthetic ()) + if (font->is_synthetic) { font = hb_font_create_sub_font (font); hb_font_set_synthetic_bold (font, 0, 0, true); @@ -178,7 +178,10 @@ struct hb_colrv1_closure_context_t : { glyphs->add (glyph_id); } void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers) - { layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); } + { + if (num_of_layers == 0) return; + layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); + } void add_palette_index (unsigned palette_index) { palette_indices->add (palette_index); } @@ -650,10 +653,10 @@ struct PaintColrLayers TRACE_SUBSET (this); auto *out = c->serializer->embed (this); if (unlikely (!out)) return_trace (false); - return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers.get (firstLayerIndex), - HB_SERIALIZE_ERROR_INT_OVERFLOW)); - return_trace (true); + uint32_t first_layer_index = numLayers ? c->plan->colrv1_layers.get (firstLayerIndex) : 0; + return_trace (c->serializer->check_assign (out->firstLayerIndex, first_layer_index, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); } bool sanitize (hb_sanitize_context_t *c) const @@ -1075,9 +1078,9 @@ struct PaintTranslate float ddx = dx + c->instancer (varIdxBase, 0); float ddy = dy + c->instancer (varIdxBase, 1); - bool p1 = c->funcs->push_translate (c->data, ddx, ddy); + c->funcs->push_translate (c->data, ddx, ddy); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 14(noVar) or 15 (Var) */ @@ -1124,9 +1127,9 @@ struct PaintScale float sx = scaleX.to_float (c->instancer (varIdxBase, 0)); float sy = scaleY.to_float (c->instancer (varIdxBase, 1)); - bool p1 = c->funcs->push_scale (c->data, sx, sy); + c->funcs->push_scale (c->data, sx, sy); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 16 (noVar) or 17(Var) */ @@ -1177,13 +1180,9 @@ struct PaintScaleAroundCenter float tCenterX = centerX + c->instancer (varIdxBase, 2); float tCenterY = centerY + c->instancer (varIdxBase, 3); - bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); - bool p2 = c->funcs->push_scale (c->data, sx, sy); - bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->funcs->push_scale_around_center (c->data, sx, sy, tCenterX, tCenterY); c->recurse (this+src); - if (p3) c->funcs->pop_transform (c->data); - if (p2) c->funcs->pop_transform (c->data); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 18 (noVar) or 19(Var) */ @@ -1228,9 +1227,9 @@ struct PaintScaleUniform TRACE_PAINT (this); float s = scale.to_float (c->instancer (varIdxBase, 0)); - bool p1 = c->funcs->push_scale (c->data, s, s); + c->funcs->push_scale (c->data, s, s); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 20 (noVar) or 21(Var) */ @@ -1278,13 +1277,9 @@ struct PaintScaleUniformAroundCenter float tCenterX = centerX + c->instancer (varIdxBase, 1); float tCenterY = centerY + c->instancer (varIdxBase, 2); - bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); - bool p2 = c->funcs->push_scale (c->data, s, s); - bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->funcs->push_scale_around_center (c->data, s, s, tCenterX, tCenterY); c->recurse (this+src); - if (p3) c->funcs->pop_transform (c->data); - if (p2) c->funcs->pop_transform (c->data); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 22 (noVar) or 23(Var) */ @@ -1328,9 +1323,9 @@ struct PaintRotate TRACE_PAINT (this); float a = angle.to_float (c->instancer (varIdxBase, 0)); - bool p1 = c->funcs->push_rotate (c->data, a); + c->funcs->push_rotate (c->data, a); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 24 (noVar) or 25(Var) */ @@ -1378,13 +1373,9 @@ struct PaintRotateAroundCenter float tCenterX = centerX + c->instancer (varIdxBase, 1); float tCenterY = centerY + c->instancer (varIdxBase, 2); - bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); - bool p2 = c->funcs->push_rotate (c->data, a); - bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->funcs->push_rotate_around_center (c->data, a, tCenterX, tCenterY); c->recurse (this+src); - if (p3) c->funcs->pop_transform (c->data); - if (p2) c->funcs->pop_transform (c->data); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 26 (noVar) or 27(Var) */ @@ -1432,9 +1423,9 @@ struct PaintSkew float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0)); float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1)); - bool p1 = c->funcs->push_skew (c->data, sx, sy); + c->funcs->push_skew (c->data, sx, sy); c->recurse (this+src); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 28(noVar) or 29 (Var) */ @@ -1485,13 +1476,9 @@ struct PaintSkewAroundCenter float tCenterX = centerX + c->instancer (varIdxBase, 2); float tCenterY = centerY + c->instancer (varIdxBase, 3); - bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY); - bool p2 = c->funcs->push_skew (c->data, sx, sy); - bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY); + c->funcs->push_skew_around_center (c->data, sx, sy, tCenterX, tCenterY); c->recurse (this+src); - if (p3) c->funcs->pop_transform (c->data); - if (p2) c->funcs->pop_transform (c->data); - if (p1) c->funcs->pop_transform (c->data); + c->funcs->pop_transform (c->data); } HBUINT8 format; /* format = 30(noVar) or 31 (Var) */ @@ -1626,7 +1613,7 @@ struct ClipBox const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION)); case 2: return_trace (u.format2.subset (c, instancer)); default:return_trace (c->default_return_value ()); @@ -1635,7 +1622,7 @@ struct ClipBox void closurev1 (hb_colrv1_closure_context_t* c) const { - switch (u.format) { + switch (u.format.v) { case 2: u.format2.closurev1 (c); return; default:return; } @@ -1644,9 +1631,9 @@ struct ClipBox template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); default:return_trace (c->default_return_value ()); @@ -1657,7 +1644,7 @@ struct ClipBox const ItemVarStoreInstancer &instancer) const { ClipBoxData clip_box; - switch (u.format) { + switch (u.format.v) { case 1: u.format1.get_clip_box (clip_box, instancer); break; @@ -1677,7 +1664,7 @@ struct ClipBox protected: union { - HBUINT8 format; /* Format identifier */ + struct { HBUINT8 v; } format; /* Format identifier */ ClipBoxFormat1 format1; ClipBoxFormat2 format2; } u; @@ -1857,9 +1844,9 @@ struct Paint template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...)); case 3: return_trace (c->dispatch (u.paintformat3, std::forward (ds)...)); @@ -1898,7 +1885,7 @@ struct Paint protected: union { - HBUINT8 format; + struct { HBUINT8 v; } format; PaintColrLayers paintformat1; NoVariable paintformat2; Variable paintformat3; @@ -2073,7 +2060,7 @@ struct delta_set_index_map_subset_plan_t outer_bit_count = 1; inner_bit_count = 1; - if (unlikely (!output_map.resize (map_count, false))) return false; + if (unlikely (!output_map.resize_dirty (map_count))) return false; for (unsigned idx = 0; idx < map_count; idx++) { @@ -2693,7 +2680,8 @@ struct COLR { ItemVarStoreInstancer instancer (get_var_store_ptr (), get_delta_set_index_map_ptr (), - hb_array (font->coords, font->num_coords)); + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0)); hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); hb_decycler_node_t node (c.glyphs_decycler); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh index 51fc1b52af4..71417fdf3cf 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Color/CPAL/CPAL.hh @@ -307,6 +307,7 @@ struct CPAL if (first_color_to_layer_index.has (first_color_record_idx)) continue; first_color_index_for_layer.push (first_color_record_idx); + if (unlikely (!c->serializer->propagate_error (first_color_index_for_layer))) return_trace (false); first_color_to_layer_index.set (first_color_record_idx, first_color_index_for_layer.length - 1); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh index 35d73c7b858..28678856373 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/Coverage.hh @@ -46,7 +46,7 @@ struct Coverage protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CoverageFormat1_3 format1; CoverageFormat2_4 format2; #ifndef HB_NO_BEYOND_64K @@ -55,7 +55,7 @@ struct Coverage #endif } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE @@ -63,9 +63,9 @@ struct Coverage bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); @@ -86,7 +86,7 @@ struct Coverage unsigned int get (hb_codepoint_t k) const { return get_coverage (k); } unsigned int get_coverage (hb_codepoint_t glyph_id) const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.get_coverage (glyph_id); case 2: return u.format2.get_coverage (glyph_id); #ifndef HB_NO_BEYOND_64K @@ -97,18 +97,38 @@ struct Coverage } } unsigned int get_coverage (hb_codepoint_t glyph_id, - hb_ot_lookup_cache_t *cache) const + hb_ot_layout_mapping_cache_t *cache) const { unsigned coverage; - if (cache && cache->get (glyph_id, &coverage)) return coverage; + if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED; coverage = get_coverage (glyph_id); - if (cache) cache->set (glyph_id, coverage); + if (cache) { + if (coverage == NOT_COVERED) + cache->set_unchecked (glyph_id, cache->MAX_VALUE); + else if (likely (coverage < cache->MAX_VALUE)) + cache->set_unchecked (glyph_id, coverage); + } + return coverage; + } + + unsigned int get_coverage_binary (hb_codepoint_t glyph_id, + hb_ot_layout_binary_cache_t *cache) const + { + unsigned coverage; + if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED; + coverage = get_coverage (glyph_id); + if (cache) { + if (coverage == NOT_COVERED) + cache->set_unchecked (glyph_id, cache->MAX_VALUE); + else + cache->set_unchecked (glyph_id, 0); + } return coverage; } unsigned get_population () const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.get_population (); case 2: return u.format2.get_population (); #ifndef HB_NO_BEYOND_64K @@ -140,11 +160,11 @@ struct Coverage last = g; if (g > max) max = g; } - u.format = !unsorted && count <= num_ranges * 3 ? 1 : 2; + u.format.v = !unsorted && count <= num_ranges * 3 ? 1 : 2; #ifndef HB_NO_BEYOND_64K if (max > 0xFFFFu) - u.format += 2; + u.format.v += 2; if (unlikely (max > 0xFFFFFFu)) #else if (unlikely (max > 0xFFFFu)) @@ -154,7 +174,7 @@ struct Coverage return_trace (false); } - switch (u.format) + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, glyphs)); case 2: return_trace (u.format2.serialize (c, glyphs)); @@ -185,7 +205,7 @@ struct Coverage bool intersects (const hb_set_t *glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersects (glyphs); case 2: return u.format2.intersects (glyphs); @@ -198,7 +218,7 @@ struct Coverage } bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersects_coverage (glyphs, index); case 2: return u.format2.intersects_coverage (glyphs, index); @@ -212,7 +232,7 @@ struct Coverage unsigned cost () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.cost (); case 2: hb_barrier (); return u.format2.cost (); #ifndef HB_NO_BEYOND_64K @@ -228,7 +248,7 @@ struct Coverage template bool collect_coverage (set_t *glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.collect_coverage (glyphs); case 2: return u.format2.collect_coverage (glyphs); @@ -244,7 +264,7 @@ struct Coverage hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))> void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersect_set (glyphs, intersect_glyphs); case 2: return u.format2.intersect_set (glyphs, intersect_glyphs); @@ -262,7 +282,7 @@ struct Coverage iter_t (const Coverage &c_ = Null (Coverage)) { hb_memset (this, 0, sizeof (*this)); - format = c_.u.format; + format = c_.u.format.v; switch (format) { case 1: u.format1.init (c_.u.format1); return; @@ -332,7 +352,7 @@ struct Coverage } iter_t __end__ () const { - iter_t it = {}; + iter_t it; it.format = format; switch (format) { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh index 97ea51089ec..122326e3024 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat1.hh @@ -41,11 +41,11 @@ struct CoverageFormat1_3 { friend struct Coverage; - protected: + public: HBUINT16 coverageFormat; /* Format identifier--format = 1 */ SortedArray16Of glyphArray; /* Array of GlyphIDs--in numerical order */ - public: + DEFINE_SIZE_ARRAY (4, glyphArray); private: diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh index 0c9bd09cbf7..8287c8d4d4d 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/Common/CoverageFormat2.hh @@ -40,7 +40,7 @@ struct CoverageFormat2_4 { friend struct Coverage; - protected: + public: HBUINT16 coverageFormat; /* Format identifier--format = 2 */ SortedArray16Of> rangeRecord; /* Array of glyph ranges--ordered by diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh index ceba37b0619..89e110990bc 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GDEF/GDEF.hh @@ -252,7 +252,7 @@ struct CaretValue hb_codepoint_t glyph_id, const ItemVariationStore &var_store) const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.get_caret_value (font, direction); case 2: return u.format2.get_caret_value (font, direction, glyph_id); case 3: return u.format3.get_caret_value (font, direction, var_store); @@ -263,9 +263,9 @@ struct CaretValue template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -275,7 +275,7 @@ struct CaretValue void collect_variation_indices (hb_collect_variation_indices_context_t *c) const { - switch (u.format) { + switch (u.format.v) { case 1: case 2: return; @@ -289,9 +289,9 @@ struct CaretValue bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); case 3: return_trace (u.format3.sanitize (c)); @@ -301,13 +301,13 @@ struct CaretValue protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CaretValueFormat1 format1; CaretValueFormat2 format2; CaretValueFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct LigGlyph @@ -519,7 +519,7 @@ struct MarkGlyphSets { bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.covers (set_index, glyph_id); default:return false; } @@ -528,7 +528,7 @@ struct MarkGlyphSets template void collect_coverage (hb_vector_t &sets) const { - switch (u.format) { + switch (u.format.v) { case 1: u.format1.collect_coverage (sets); return; default:return; } @@ -537,7 +537,7 @@ struct MarkGlyphSets void collect_used_mark_sets (const hb_set_t& glyph_set, hb_set_t& used_mark_sets /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 1: u.format1.collect_used_mark_sets (glyph_set, used_mark_sets); return; default:return; } @@ -546,7 +546,7 @@ struct MarkGlyphSets bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.subset (c)); default:return_trace (false); } @@ -555,9 +555,9 @@ struct MarkGlyphSets bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); default:return_trace (true); } @@ -565,11 +565,11 @@ struct MarkGlyphSets protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkGlyphSetsFormat1 format1; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; @@ -977,7 +977,7 @@ struct GDEF } #ifndef HB_NO_GDEF_CACHE - table->get_mark_glyph_sets ().collect_coverage (mark_glyph_set_digests); + table->get_mark_glyph_sets ().collect_coverage (mark_glyph_sets); #endif } ~accelerator_t () { table.destroy (); } @@ -1002,18 +1002,34 @@ struct GDEF } + HB_ALWAYS_INLINE bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const { return #ifndef HB_NO_GDEF_CACHE - mark_glyph_set_digests[set_index].may_have (glyph_id) && + // We can access arrayZ directly because of sanitize_lookup_props() guarantee. + mark_glyph_sets.arrayZ[set_index].may_have (glyph_id) && #endif - table->mark_set_covers (set_index, glyph_id); + table->mark_set_covers (set_index, glyph_id) + ; + } + + unsigned sanitize_lookup_props (unsigned lookup_props) const + { +#ifndef HB_NO_GDEF_CACHE + if (lookup_props & LookupFlag::UseMarkFilteringSet && + (lookup_props >> 16) >= mark_glyph_sets.length) + { + // Invalid mark filtering set index; unset the flag. + lookup_props &= ~LookupFlag::UseMarkFilteringSet; + } +#endif + return lookup_props; } hb_blob_ptr_t table; #ifndef HB_NO_GDEF_CACHE - hb_vector_t mark_glyph_set_digests; + hb_vector_t mark_glyph_sets; mutable hb_cache_t<21, 3> glyph_props_cache; static_assert (sizeof (glyph_props_cache) == 512, ""); #endif diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh index 7802e397f4c..1938803fa7c 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/Anchor.hh @@ -13,20 +13,20 @@ struct Anchor { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ AnchorFormat1 format1; AnchorFormat2 format2; AnchorFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); case 3: return_trace (u.format3.sanitize (c)); @@ -38,7 +38,7 @@ struct Anchor float *x, float *y) const { *x = *y = 0; - switch (u.format) { + switch (u.format.v) { case 1: u.format1.get_anchor (c, glyph_id, x, y); return; case 2: u.format2.get_anchor (c, glyph_id, x, y); return; case 3: u.format3.get_anchor (c, glyph_id, x, y); return; @@ -49,7 +49,7 @@ struct Anchor bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); case 2: if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) @@ -66,7 +66,7 @@ struct Anchor void collect_variation_indices (hb_collect_variation_indices_context_t *c) const { - switch (u.format) { + switch (u.format.v) { case 1: case 2: return; case 3: diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh index 61bd90310a5..c49705bea0e 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorFormat3.hh @@ -37,12 +37,12 @@ struct AnchorFormat3 *x = font->em_fscale_x (xCoordinate); *y = font->em_fscale_y (yCoordinate); - if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this)) + if ((font->x_ppem || font->has_nonzero_coords) && xDeviceTable.sanitize (&c->sanitizer, this)) { hb_barrier (); *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache); } - if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this)) + if ((font->y_ppem || font->has_nonzero_coords) && yDeviceTable.sanitize (&c->sanitizer, this)) { hb_barrier (); *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache); @@ -91,10 +91,13 @@ struct AnchorFormat3 } } - /* in case that all axes are pinned or no variations after instantiation, - * both var_idxes will be mapped to HB_OT_LAYOUT_NO_VARIATIONS_INDEX */ - if (x_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX && - y_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + + bool no_downgrade = (!xDeviceTable.is_null () && !(this+xDeviceTable).is_variation_device ()) || + x_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX || + y_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX || + (!yDeviceTable.is_null () && !(this+yDeviceTable).is_variation_device ()); + + if (!no_downgrade) return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); if (!c->serializer->embed (xDeviceTable)) return_trace (false); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh index 9da9fff50ba..128ced6c176 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/AnchorMatrix.hh @@ -77,6 +77,13 @@ struct AnchorMatrix return_trace (true); } + + bool offset_is_null (unsigned row, unsigned col, unsigned num_cols) const + { + if (unlikely (row >= rows || col >= num_cols)) return true; + auto &offset = matrixZ[row * num_cols + col]; + return offset.is_null (); + } }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh index 0105a9b8542..38a29dd9ed5 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePos.hh @@ -11,7 +11,7 @@ struct CursivePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CursivePosFormat1 format1; } u; @@ -19,9 +19,9 @@ struct CursivePos template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh index 361aaed658a..f5a09e07d73 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/CursivePosFormat1.hh @@ -50,8 +50,9 @@ struct EntryExitRecord DEFINE_SIZE_STATIC (4); }; -static void -reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) { +static inline void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) +{ int chain = pos[i].attach_chain(), type = pos[i].attach_type(); if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE))) return; @@ -130,7 +131,7 @@ struct CursivePosFormat1 unlikely (!this_record.entryAnchor.sanitize (&c->sanitizer, this))) return_trace (false); hb_barrier (); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.reset_fast (buffer->idx); unsigned unsafe_from; if (unlikely (!skippy_iter.prev (&unsafe_from))) @@ -229,8 +230,13 @@ struct CursivePosFormat1 */ reverse_cursive_minor_offset (pos, child, c->direction, parent); - pos[child].attach_type() = ATTACH_TYPE_CURSIVE; pos[child].attach_chain() = (int) parent - (int) child; + if (pos[child].attach_chain() != (int) parent - (int) child) + { + pos[child].attach_chain() = 0; + goto overflow; + } + pos[child].attach_type() = ATTACH_TYPE_CURSIVE; buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) pos[child].y_offset = y_offset; @@ -256,6 +262,7 @@ struct CursivePosFormat1 i, j); } + overflow: buffer->idx++; return_trace (true); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh index ce3f74d8c3b..b80f606f7b5 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/GPOS.hh @@ -80,9 +80,8 @@ propagate_attachment_offsets (hb_glyph_position_t *pos, { /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate * offset of glyph they are attached to. */ - int chain = pos[i].attach_chain(), type = pos[i].attach_type(); - if (likely (!chain)) - return; + int chain = pos[i].attach_chain(); + int type = pos[i].attach_type(); pos[i].attach_chain() = 0; @@ -94,7 +93,8 @@ propagate_attachment_offsets (hb_glyph_position_t *pos, if (unlikely (!nesting_level)) return; - propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); + if (pos[j].attach_chain()) + propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE)); @@ -110,17 +110,37 @@ propagate_attachment_offsets (hb_glyph_position_t *pos, pos[i].x_offset += pos[j].x_offset; pos[i].y_offset += pos[j].y_offset; - assert (j < i); - if (HB_DIRECTION_IS_FORWARD (direction)) - for (unsigned int k = j; k < i; k++) { - pos[i].x_offset -= pos[k].x_advance; - pos[i].y_offset -= pos[k].y_advance; - } - else - for (unsigned int k = j + 1; k < i + 1; k++) { - pos[i].x_offset += pos[k].x_advance; - pos[i].y_offset += pos[k].y_advance; - } + // i is the position of the mark; j is the base. + if (j < i) + { + /* This is the common case: mark follows base. + * And currently the only way in OpenType. */ + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = j; k < i; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + else + for (unsigned int k = j + 1; k < i + 1; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + } + else // j > i + { + /* This can happen with `kerx`: a mark attaching + * to a base after it in the logical order. */ + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = i; k < j; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + else + for (unsigned int k = i + 1; k < j + 1; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + } } } @@ -149,8 +169,20 @@ GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) /* Handle attachments */ if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) - for (unsigned i = 0; i < len; i++) - propagate_attachment_offsets (pos, len, i, direction); + { + auto *pos = buffer->pos; + // https://github.com/harfbuzz/harfbuzz/issues/5514 + if (HB_DIRECTION_IS_FORWARD (direction)) + { + for (unsigned i = 0; i < len; i++) + if (pos[i].attach_chain()) + propagate_attachment_offsets (pos, len, i, direction); + } else { + for (unsigned i = len; i-- > 0; ) + if (pos[i].attach_chain()) + propagate_attachment_offsets (pos, len, i, direction); + } + } if (unlikely (font->slant_xy) && HB_DIRECTION_IS_HORIZONTAL (direction)) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh index eecdb95a95f..113b693dc3e 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/LigatureArray.hh @@ -19,22 +19,30 @@ struct LigatureArray : List16OfOffset16To bool subset (hb_subset_context_t *c, Iterator coverage, unsigned class_count, - const hb_map_t *klass_mapping) const + const hb_map_t *klass_mapping, + hb_sorted_vector_t &new_coverage /* OUT */) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = c->plan->glyph_map_gsub; auto *out = c->serializer->start_embed (this); if (unlikely (!c->serializer->extend_min (out))) return_trace (false); bool ret = false; for (const auto _ : + hb_zip (coverage, *this) - | hb_filter (glyphset, hb_first)) + | hb_filter (glyph_map, hb_first)) { + const LigatureAttach& src = (this + _.second); + bool non_empty = + hb_range (src.rows * class_count) + | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) + | hb_map ([&] (const unsigned index) { return !src.offset_is_null (index / class_count, index % class_count, class_count); }) + | hb_any; + + if (!non_empty) continue; + auto *matrix = out->serialize_append (c->serializer); if (unlikely (!matrix)) return_trace (false); - const LigatureAttach& src = (this + _.second); auto indexes = + hb_range (src.rows * class_count) | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) @@ -44,6 +52,9 @@ struct LigatureArray : List16OfOffset16To this, src.rows, indexes); + + hb_codepoint_t new_gid = glyph_map.get (_.first); + new_coverage.push (new_gid); } return_trace (ret); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh index abae8f1c607..bddc5e7fe9e 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkArray.hh @@ -47,10 +47,15 @@ struct MarkArray : Array16Of /* Array of MarkRecords--in Cove } hb_glyph_position_t &o = buffer->cur_pos(); + o.attach_chain() = (int) glyph_pos - (int) buffer->idx; + if (o.attach_chain() != (int) glyph_pos - (int) buffer->idx) + { + o.attach_chain() = 0; + goto overflow; + } + o.attach_type() = ATTACH_TYPE_MARK; o.x_offset = roundf (base_x - mark_x); o.y_offset = roundf (base_y - mark_y); - o.attach_type() = ATTACH_TYPE_MARK; - o.attach_chain() = (int) glyph_pos - (int) buffer->idx; buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) @@ -60,6 +65,7 @@ struct MarkArray : Array16Of /* Array of MarkRecords--in Cove c->buffer->idx, glyph_pos); } + overflow: buffer->idx++; return_trace (true); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh index b1d1118a86b..65f343bda8b 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePos.hh @@ -11,7 +11,7 @@ struct MarkBasePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkBasePosFormat1_2 format1; #ifndef HB_NO_BEYOND_64K MarkBasePosFormat1_2 format2; @@ -22,9 +22,9 @@ struct MarkBasePos template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh index e633f7a1be1..ad071f327ea 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkBasePosFormat1.hh @@ -119,7 +119,7 @@ struct MarkBasePosFormat1_2 /* Now we search backwards for a non-mark glyph. * We don't use skippy_iter.prev() to avoid O(n^2) behavior. */ - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); if (c->last_base_until > buffer->idx) @@ -209,19 +209,22 @@ struct MarkBasePosFormat1_2 ; new_coverage.reset (); - + base_iter - | hb_map (hb_first) - | hb_map (glyph_map) - | hb_sink (new_coverage) - ; - - if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) - return_trace (false); - hb_sorted_vector_t base_indexes; - for (const unsigned row : + base_iter - | hb_map (hb_second)) + auto &base_array = (this+baseArray); + for (const auto _ : + base_iter) { + unsigned row = _.second; + bool non_empty = + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return !base_array.offset_is_null (row, col, (unsigned) classCount); }) + | hb_any + ; + + if (!non_empty) continue; + + hb_codepoint_t new_g = glyph_map.get ( _.first); + new_coverage.push (new_g); + + hb_range ((unsigned) classCount) | hb_filter (klass_mapping) | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) @@ -229,8 +232,12 @@ struct MarkBasePosFormat1_2 ; } + if (!new_coverage) return_trace (false); + if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + return_trace (out->baseArray.serialize_subset (c, baseArray, this, - base_iter.len (), + new_coverage.length, base_indexes.iter ())); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh index b10102880c0..ee237eb479f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPos.hh @@ -11,7 +11,7 @@ struct MarkLigPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkLigPosFormat1_2 format1; #ifndef HB_NO_BEYOND_64K MarkLigPosFormat1_2 format2; @@ -22,9 +22,9 @@ struct MarkLigPos template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh index cf4cbae9a3f..509a26c2485 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkLigPosFormat1.hh @@ -101,7 +101,7 @@ struct MarkLigPosFormat1_2 /* Now we search backwards for a non-mark glyph */ - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); if (c->last_base_until > buffer->idx) @@ -200,19 +200,13 @@ struct MarkLigPosFormat1_2 &klass_mapping))) return_trace (false); - auto new_ligature_coverage = - + hb_iter (this + ligatureCoverage) - | hb_take ((this + ligatureArray).len) - | hb_map_retains_sorting (glyph_map) - | hb_filter ([] (hb_codepoint_t glyph) { return glyph != HB_MAP_VALUE_INVALID; }) - ; - - if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage)) + hb_sorted_vector_t new_lig_coverage; + if (!out->ligatureArray.serialize_subset (c, ligatureArray, this, + hb_iter (this+ligatureCoverage), + classCount, &klass_mapping, new_lig_coverage)) return_trace (false); - return_trace (out->ligatureArray.serialize_subset (c, ligatureArray, this, - hb_iter (this+ligatureCoverage), - classCount, &klass_mapping)); + return_trace (out->ligatureCoverage.serialize_serialize (c->serializer, new_lig_coverage.iter ())); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh index e0d9eca0280..c06f013cdce 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPos.hh @@ -11,7 +11,7 @@ struct MarkMarkPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkMarkPosFormat1_2 format1; #ifndef HB_NO_BEYOND_64K MarkMarkPosFormat1_2 format2; @@ -22,9 +22,9 @@ struct MarkMarkPos template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh index 6519e79b443..c93dbbb3f06 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/MarkMarkPosFormat1.hh @@ -100,7 +100,7 @@ struct MarkMarkPosFormat1_2 if (likely (mark1_index == NOT_COVERED)) return_trace (false); /* now we search backwards for a suitable mark glyph until a non-mark glyph */ - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.reset_fast (buffer->idx); skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags); unsigned unsafe_from; @@ -196,19 +196,23 @@ struct MarkMarkPosFormat1_2 ; new_coverage.reset (); - + mark2_iter - | hb_map (hb_first) - | hb_map (glyph_map) - | hb_sink (new_coverage) - ; - - if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) - return_trace (false); - hb_sorted_vector_t mark2_indexes; - for (const unsigned row : + mark2_iter - | hb_map (hb_second)) + auto &mark2_array = (this+mark2Array); + for (const auto _ : + mark2_iter) { + unsigned row = _.second; + + bool non_empty = + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return !mark2_array.offset_is_null (row, col, (unsigned) classCount); }) + | hb_any + ; + + if (!non_empty) continue; + + hb_codepoint_t new_g = glyph_map.get ( _.first); + new_coverage.push (new_g); + + hb_range ((unsigned) classCount) | hb_filter (klass_mapping) | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) @@ -216,6 +220,10 @@ struct MarkMarkPosFormat1_2 ; } + if (!new_coverage) return_trace (false); + if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + return_trace (out->mark2Array.serialize_subset (c, mark2Array, this, mark2_iter.len (), mark2_indexes.iter ())); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh index e3794ea9ed5..bf7fff7face 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPos.hh @@ -12,7 +12,7 @@ struct PairPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ PairPosFormat1_3 format1; PairPosFormat2_4 format2; #ifndef HB_NO_BEYOND_64K @@ -25,9 +25,9 @@ struct PairPos template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh index 2748882f527..1c067bde86f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat1.hh @@ -103,52 +103,35 @@ struct PairPosFormat1_3 const Coverage &get_coverage () const { return this+coverage; } - unsigned cache_cost () const + struct external_cache_t { - return (this+coverage).cost (); - } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) + hb_ot_layout_mapping_cache_t coverage; + }; + void *external_cache_create () const { - switch (op) + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) { - case hb_ot_lookup_cache_op_t::CREATE: - { - hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t)); - if (likely (cache)) - cache->clear (); - return cache; - } - case hb_ot_lookup_cache_op_t::ENTER: - return (void *) true; - case hb_ot_lookup_cache_op_t::LEAVE: - return nullptr; - case hb_ot_lookup_cache_op_t::DESTROY: - { - hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p; - hb_free (cache); - return nullptr; - } + cache->coverage.clear (); } - return nullptr; + return cache; } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { TRACE_APPLY (this); hb_buffer_t *buffer = c->buffer; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr; - unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache); + external_cache_t *cache = (external_cache_t *) external_cache; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr); #else unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); #endif if (index == NOT_COVERED) return_trace (false); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.reset_fast (buffer->idx); unsigned unsafe_to; if (unlikely (!skippy_iter.next (&unsafe_to))) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh index d85b1ac2c17..ce731450f41 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/PairPosFormat2.hh @@ -123,63 +123,39 @@ struct PairPosFormat2_4 : ValueBase const Coverage &get_coverage () const { return this+coverage; } - struct pair_pos_cache_t + struct external_cache_t { - hb_ot_lookup_cache_t coverage; - hb_ot_lookup_cache_t first; - hb_ot_lookup_cache_t second; + hb_ot_layout_mapping_cache_t coverage; + hb_ot_layout_mapping_cache_t first; + hb_ot_layout_mapping_cache_t second; }; - - unsigned cache_cost () const + void *external_cache_create () const { - return (this+coverage).cost () + (this+classDef1).cost () + (this+classDef2).cost (); - } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) - { - switch (op) + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) { - case hb_ot_lookup_cache_op_t::CREATE: - { - pair_pos_cache_t *cache = (pair_pos_cache_t *) hb_malloc (sizeof (pair_pos_cache_t)); - if (likely (cache)) - { - cache->coverage.clear (); - cache->first.clear (); - cache->second.clear (); - } - return cache; - } - case hb_ot_lookup_cache_op_t::ENTER: - return (void *) true; - case hb_ot_lookup_cache_op_t::LEAVE: - return nullptr; - case hb_ot_lookup_cache_op_t::DESTROY: - { - pair_pos_cache_t *cache = (pair_pos_cache_t *) p; - hb_free (cache); - return nullptr; - } + cache->coverage.clear (); + cache->first.clear (); + cache->second.clear (); } - return nullptr; + return cache; } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { TRACE_APPLY (this); hb_buffer_t *buffer = c->buffer; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - pair_pos_cache_t *cache = cached ? (pair_pos_cache_t *) c->lookup_accel->cache : nullptr; + external_cache_t *cache = (external_cache_t *) external_cache; unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr); #else unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); #endif if (index == NOT_COVERED) return_trace (false); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_input; skippy_iter.reset_fast (buffer->idx); unsigned unsafe_to; if (unlikely (!skippy_iter.next (&unsafe_to))) diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh index a0243a218c5..30fc1aacda8 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/SinglePos.hh @@ -12,7 +12,7 @@ struct SinglePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ SinglePosFormat1 format1; SinglePosFormat2 format2; } u; @@ -41,7 +41,7 @@ struct SinglePos const hb_hashmap_t> *layout_variation_idx_delta_map, unsigned newFormat) { - if (unlikely (!c->extend_min (u.format))) return; + if (unlikely (!c->extend_min (u.format.v))) return; unsigned format = 2; ValueFormat new_format; new_format = newFormat; @@ -49,8 +49,8 @@ struct SinglePos if (glyph_val_iter_pairs) format = get_format (glyph_val_iter_pairs); - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: u.format1.serialize (c, src, glyph_val_iter_pairs, @@ -70,9 +70,9 @@ struct SinglePos template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); default:return_trace (c->default_return_value ()); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh index 731d1ffca1a..b961a5139d8 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GPOS/ValueFormat.hh @@ -56,9 +56,14 @@ struct ValueFormat : HBUINT16 * PosTable (may be NULL) */ #endif - IntType& operator = (uint16_t i) { v = i; return *this; } + NumType& operator = (uint16_t i) { v = i; return *this; } - unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } + // Note: spec says skip 2 bytes per bit in the valueformat. But reports + // from Microsoft developers indicate that only the fields that are + // currently defined are counted. We don't expect any new fields to + // be added to ValueFormat. As such, we use the faster hb_popcount8 + // that only processes the lowest 8 bits. + unsigned int get_len () const { return hb_popcount8 ((uint8_t) *this); } unsigned int get_size () const { return get_len () * Value::static_size; } hb_vector_t get_device_table_indices () const { @@ -111,8 +116,8 @@ struct ValueFormat : HBUINT16 if (!has_device ()) return ret; - bool use_x_device = font->x_ppem || font->num_coords; - bool use_y_device = font->y_ppem || font->num_coords; + bool use_x_device = font->x_ppem || font->has_nonzero_coords; + bool use_y_device = font->y_ppem || font->has_nonzero_coords; if (!use_x_device && !use_y_device) return ret; diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh index b5d506f36f9..f13c5e7e251 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSet.hh @@ -91,6 +91,19 @@ struct AlternateSet return alternates.len; } + void + collect_alternates (hb_codepoint_t gid, + hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) const + { + + hb_enumerate (alternates) + | hb_map ([gid] (hb_pair_t _) { return hb_pair (gid + (_.first << 24), _.second); }) + | hb_apply ([&] (const hb_pair_t &p) -> void + { _hb_collect_glyph_alternates_add (p.first, p.second, + alternate_count, alternate_glyphs); }) + ; + } + template bool serialize (hb_serialize_context_t *c, diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh index 8951f5a7a17..a43c75c8f4e 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubst.hh @@ -12,7 +12,7 @@ struct AlternateSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ AlternateSubstFormat1_2 format1; #ifndef HB_NO_BEYOND_64K AlternateSubstFormat1_2 format2; @@ -23,9 +23,9 @@ struct AlternateSubst template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); @@ -42,10 +42,10 @@ struct AlternateSubst hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list)); default:return_trace (false); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh index 421a6e06627..7a3a2511b7f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/AlternateSubstFormat1.hh @@ -69,6 +69,19 @@ struct AlternateSubstFormat1_2 { return (this+alternateSet[(this+coverage).get_coverage (gid)]) .get_alternates (start_offset, alternate_count, alternate_glyphs); } + void + collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) const + { + + hb_iter (alternateSet) + | hb_map (hb_add (this)) + | hb_zip (this+coverage) + | hb_apply ([&] (const hb_pair_t &, hb_codepoint_t> _) { + _.first.collect_alternates (_.second, alternate_count, alternate_glyphs); + }) + ; + } + bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh index 726da458fac..7bc98d31f32 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Ligature.hh @@ -44,6 +44,18 @@ struct Ligature c->output->add (ligGlyph); } + template + void collect_second (set_t &s) const + { + if (unlikely (!component.get_length ())) + { + // A ligature without any components. Anything matches. + s = set_t::full (); + return; + } + s.add (component.arrayZ[0]); + } + bool would_apply (hb_would_apply_context_t *c) const { if (c->len != component.lenP1) @@ -91,15 +103,6 @@ struct Ligature unsigned int total_component_count = 0; if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false; - unsigned match_positions_stack[4]; - unsigned *match_positions = match_positions_stack; - if (unlikely (count > ARRAY_LENGTH (match_positions_stack))) - { - match_positions = (unsigned *) hb_malloc (hb_max (count, 1u) * sizeof (unsigned)); - if (unlikely (!match_positions)) - return_trace (false); - } - unsigned int match_end = 0; if (likely (!match_input (c, count, @@ -107,12 +110,9 @@ struct Ligature match_glyph, nullptr, &match_end, - match_positions, &total_component_count))) { c->buffer->unsafe_to_concat (c->buffer->idx, match_end); - if (match_positions != match_positions_stack) - hb_free (match_positions); return_trace (false); } @@ -129,10 +129,10 @@ struct Ligature match_end += delta; for (unsigned i = 0; i < count; i++) { - match_positions[i] += delta; + c->match_positions[i] += delta; if (i) *p++ = ','; - snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]); + snprintf (p, sizeof(buf) - (p - buf), "%u", c->match_positions[i]); p += strlen(p); } @@ -143,7 +143,6 @@ struct Ligature ligate_input (c, count, - match_positions, match_end, ligGlyph, total_component_count); @@ -156,8 +155,6 @@ struct Ligature pos); } - if (match_positions != match_positions_stack) - hb_free (match_positions); return_trace (true); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh index ff0ffce94d7..81c5c2bcfe4 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSet.hh @@ -11,11 +11,11 @@ namespace GSUB_impl { template struct LigatureSet { - protected: + public: Array16OfOffset16To> ligature; /* Array LigatureSet tables * ordered by preference */ - public: + DEFINE_SIZE_ARRAY (2, ligature); bool sanitize (hb_sanitize_context_t *c) const @@ -62,6 +62,15 @@ struct LigatureSet ; } + template + void collect_seconds (set_t &s) const + { + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([&s] (const Ligature &_) { _.collect_second (s); }) + ; + } + bool would_apply (hb_would_apply_context_t *c) const { return @@ -72,14 +81,14 @@ struct LigatureSet ; } - bool apply (hb_ot_apply_context_t *c) const + bool apply (hb_ot_apply_context_t *c, const hb_set_digest_t *seconds = nullptr) const { TRACE_APPLY (this); unsigned int num_ligs = ligature.len; #ifndef HB_NO_OT_RULESETS_FAST_PATH - if (HB_OPTIMIZE_SIZE_VAL || num_ligs <= 4) + if (HB_OPTIMIZE_SIZE_VAL || num_ligs <= 1) #endif { slow: @@ -91,21 +100,21 @@ struct LigatureSet return_trace (false); } - /* This version is optimized for speed by matching the first component + /* This version is optimized for speed by matching the second component * of the ligature here, instead of calling into the ligation code. * * This is replicated in ChainRuleSet and RuleSet. */ - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); unsigned unsafe_to; - hb_codepoint_t first = (unsigned) -1; + hb_codepoint_t second = (unsigned) -1; bool matched = skippy_iter.next (&unsafe_to); if (likely (matched)) { - first = c->buffer->info[skippy_iter.idx].codepoint; + second = c->buffer->info[skippy_iter.idx].codepoint; unsafe_to = skippy_iter.idx + 1; if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) @@ -118,13 +127,14 @@ struct LigatureSet else goto slow; + if (seconds && !seconds->may_have (second)) + return_trace (false); bool unsafe_to_concat = false; - for (unsigned int i = 0; i < num_ligs; i++) { const auto &lig = this+ligature.arrayZ[i]; if (unlikely (lig.component.lenP1 <= 1) || - lig.component.arrayZ[0] == first) + lig.component.arrayZ[0] == second) { if (lig.apply (c)) { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh index cffa910295f..22ce9b79d7f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubst.hh @@ -12,7 +12,7 @@ struct LigatureSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ LigatureSubstFormat1_2 format1; #ifndef HB_NO_BEYOND_64K LigatureSubstFormat1_2 format2; @@ -23,9 +23,9 @@ struct LigatureSubst template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); @@ -45,10 +45,10 @@ struct LigatureSubst hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, first_glyphs, ligature_per_first_glyph_count_list, diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh index 6ae24b33754..909ddca220f 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/LigatureSubstFormat1.hh @@ -78,52 +78,44 @@ struct LigatureSubstFormat1_2 return lig_set.would_apply (c); } - unsigned cache_cost () const + struct external_cache_t { - return (this+coverage).cost (); - } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) + hb_ot_layout_mapping_cache_t coverage; + hb_set_digest_t seconds; + }; + void *external_cache_create () const { - switch (op) + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) { - case hb_ot_lookup_cache_op_t::CREATE: - { - hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t)); - if (likely (cache)) - cache->clear (); - return cache; - } - case hb_ot_lookup_cache_op_t::ENTER: - return (void *) true; - case hb_ot_lookup_cache_op_t::LEAVE: - return nullptr; - case hb_ot_lookup_cache_op_t::DESTROY: - { - hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p; - hb_free (cache); - return nullptr; - } + cache->coverage.clear (); + + cache->seconds.init (); + + hb_iter (ligatureSet) + | hb_map (hb_add (this)) + | hb_apply ([cache] (const LigatureSet &_) { _.collect_seconds (cache->seconds); }) + ; } - return nullptr; + return cache; } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { TRACE_APPLY (this); hb_buffer_t *buffer = c->buffer; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr; - unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache); + external_cache_t *cache = (external_cache_t *) external_cache; + const hb_set_digest_t *seconds = cache ? &cache->seconds : nullptr; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr); #else - unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + const hb_set_digest_t *seconds = nullptr; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); #endif if (index == NOT_COVERED) return_trace (false); const auto &lig_set = this+ligatureSet[index]; - return_trace (lig_set.apply (c)); + return_trace (lig_set.apply (c, seconds)); } bool serialize (hb_serialize_context_t *c, diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh index cf3d754e3cc..8fb663825b5 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/MultipleSubst.hh @@ -12,7 +12,7 @@ struct MultipleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MultipleSubstFormat1_2 format1; #ifndef HB_NO_BEYOND_64K MultipleSubstFormat1_2 format2; @@ -24,9 +24,9 @@ struct MultipleSubst template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); @@ -41,10 +41,10 @@ struct MultipleSubst Iterator it) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, it)); default:return_trace (false); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh index 5ad463fea79..e33148d770b 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/ReverseChainSingleSubst.hh @@ -12,7 +12,7 @@ struct ReverseChainSingleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ReverseChainSingleSubstFormat1 format1; } u; @@ -20,9 +20,9 @@ struct ReverseChainSingleSubst template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh index a5e93a98bef..b3295bee16d 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/Sequence.hh @@ -115,7 +115,7 @@ struct Sequence for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++) { - if (buf < p) + if (buf < p && sizeof(buf) - 1u > unsigned (p - buf)) *p++ = ','; snprintf (p, sizeof(buf) - (p - buf), "%u", i); p += strlen(p); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh index b84259e7f00..323eb4d0f90 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubst.hh @@ -13,7 +13,7 @@ struct SingleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ SingleSubstFormat1_3 format1; SingleSubstFormat2_4 format2; #ifndef HB_NO_BEYOND_64K @@ -27,9 +27,9 @@ struct SingleSubst template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); #ifndef HB_NO_BEYOND_64K @@ -47,7 +47,7 @@ struct SingleSubst Iterator glyphs) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned format = 2; unsigned delta = 0; if (glyphs) @@ -71,8 +71,8 @@ struct SingleSubst if (!hb_all (++(+glyphs), delta, get_delta)) format += 1; } - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, + glyphs | hb_map_retains_sorting (hb_first), diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh index be6cd820d28..5ee2c1d1f46 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat1.hh @@ -123,6 +123,21 @@ struct SingleSubstFormat1_3 return 1; } + void + collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) const + { + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + + hb_iter (this+coverage) + | hb_map ([d, mask] (hb_codepoint_t g) { return hb_pair (g, (g + d) & mask); }) + | hb_apply ([&] (const hb_pair_t &p) -> void + { _hb_collect_glyph_alternates_add (p.first, p.second, + alternate_count, alternate_glyphs); }) + ; + } + bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh index e9096460451..0d51d130fee 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/GSUB/SingleSubstFormat2.hh @@ -100,6 +100,17 @@ struct SingleSubstFormat2_4 return 1; } + void + collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) const + { + + hb_zip (this+coverage, substitute) + | hb_apply ([&] (const hb_pair_t &p) -> void + { _hb_collect_glyph_alternates_add (p.first, p.second, + alternate_count, alternate_glyphs); }) + ; + } + bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh index 527f64114b4..5cf9eb368aa 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Layout/types.hh @@ -29,8 +29,11 @@ #ifndef OT_LAYOUT_TYPES_HH #define OT_LAYOUT_TYPES_HH -using hb_ot_lookup_cache_t = hb_cache_t<15, 8, 7>; -static_assert (sizeof (hb_ot_lookup_cache_t) == 256, ""); +using hb_ot_layout_mapping_cache_t = hb_cache_t<16, 8, 8>; +static_assert (sizeof (hb_ot_layout_mapping_cache_t) == 512, ""); + +using hb_ot_layout_binary_cache_t = hb_cache_t<14, 1, 8>; +static_assert (sizeof (hb_ot_layout_binary_cache_t) == 256, ""); namespace OT { namespace Layout { diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.cc b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.cc new file mode 100644 index 00000000000..f0e7f934579 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.cc @@ -0,0 +1,421 @@ +#include "VARC.hh" + +#ifndef HB_NO_VAR_COMPOSITES + +#include "../../../hb-draw.hh" +#include "../../../hb-ot-layout-common.hh" +#include "../../../hb-ot-layout-gdef-table.hh" + +namespace OT { + +//namespace Var { + + +#ifndef HB_NO_DRAW + +struct hb_transforming_pen_context_t +{ + hb_transform_t<> transform; + hb_draw_funcs_t *dfuncs; + void *data; + hb_draw_state_t *st; +}; + +static void +hb_transforming_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->transform.transform_point (to_x, to_y); + + c->dfuncs->move_to (c->data, *c->st, to_x, to_y); +} + +static void +hb_transforming_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->transform.transform_point (to_x, to_y); + + c->dfuncs->line_to (c->data, *c->st, to_x, to_y); +} + +static void +hb_transforming_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->transform.transform_point (control_x, control_y); + c->transform.transform_point (to_x, to_y); + + c->dfuncs->quadratic_to (c->data, *c->st, control_x, control_y, to_x, to_y); +} + +static void +hb_transforming_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->transform.transform_point (control1_x, control1_y); + c->transform.transform_point (control2_x, control2_y); + c->transform.transform_point (to_x, to_y); + + c->dfuncs->cubic_to (c->data, *c->st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); +} + +static void +hb_transforming_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) +{ + hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + + c->dfuncs->close_path (c->data, *c->st); +} + +static inline void free_static_transforming_pen_funcs (); + +static struct hb_transforming_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + hb_draw_funcs_set_move_to_func (funcs, hb_transforming_pen_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_transforming_pen_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_transforming_pen_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_transforming_pen_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_transforming_pen_close_path, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_transforming_pen_funcs); + + return funcs; + } +} static_transforming_pen_funcs; + +static inline +void free_static_transforming_pen_funcs () +{ + static_transforming_pen_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_transforming_pen_get_funcs () +{ + return static_transforming_pen_funcs.get_unconst (); +} + +hb_ubytes_t +VarComponent::get_path_at (const hb_varc_context_t &c, + hb_codepoint_t parent_gid, + hb_array_t coords, + hb_transform_t<> total_transform, + hb_ubytes_t total_record, + hb_scalar_cache_t *cache) const +{ + const unsigned char *end = total_record.arrayZ + total_record.length; + const unsigned char *record = total_record.arrayZ; + + auto &VARC = *c.font->face->table.VARC->table; + auto &varStore = &VARC+VARC.varStore; + +#define READ_UINT32VAR(name) \ + HB_STMT_START { \ + if (unlikely (unsigned (end - record) < HBUINT32VAR::min_size)) return hb_ubytes_t (); \ + hb_barrier (); \ + auto &varint = * (const HBUINT32VAR *) record; \ + unsigned size = varint.get_size (); \ + if (unlikely (unsigned (end - record) < size)) return hb_ubytes_t (); \ + name = (uint32_t) varint; \ + record += size; \ + } HB_STMT_END + + uint32_t flags; + READ_UINT32VAR (flags); + + // gid + + hb_codepoint_t gid = 0; + if (flags & (unsigned) flags_t::GID_IS_24BIT) + { + if (unlikely (unsigned (end - record) < HBGlyphID24::static_size)) + return hb_ubytes_t (); + hb_barrier (); + gid = * (const HBGlyphID24 *) record; + record += HBGlyphID24::static_size; + } + else + { + if (unlikely (unsigned (end - record) < HBGlyphID16::static_size)) + return hb_ubytes_t (); + hb_barrier (); + gid = * (const HBGlyphID16 *) record; + record += HBGlyphID16::static_size; + } + + // Condition + bool show = true; + if (flags & (unsigned) flags_t::HAVE_CONDITION) + { + unsigned conditionIndex; + READ_UINT32VAR (conditionIndex); + const auto &condition = (&VARC+VARC.conditionList)[conditionIndex]; + auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache); + show = condition.evaluate (coords.arrayZ, coords.length, &instancer); + } + + // Axis values + + auto &axisIndices = c.scratch.axisIndices; + axisIndices.clear (); + auto &axisValues = c.scratch.axisValues; + axisValues.clear (); + if (flags & (unsigned) flags_t::HAVE_AXES) + { + unsigned axisIndicesIndex; + READ_UINT32VAR (axisIndicesIndex); + axisIndices.extend ((&VARC+VARC.axisIndicesList)[axisIndicesIndex]); + axisValues.resize (axisIndices.length); + const HBUINT8 *p = (const HBUINT8 *) record; + TupleValues::decompile (p, axisValues, (const HBUINT8 *) end); + record = (const unsigned char *) p; + } + + // Apply variations if any + if (flags & (unsigned) flags_t::AXIS_VALUES_HAVE_VARIATION) + { + uint32_t axisValuesVarIdx; + READ_UINT32VAR (axisValuesVarIdx); + if (show && coords && !axisValues.in_error ()) + varStore.get_delta (axisValuesVarIdx, coords, axisValues.as_array (), cache); + } + + auto component_coords = coords; + /* Copying coords is expensive; so we have put an arbitrary + * limit on the max number of coords for now. */ + if ((flags & (unsigned) flags_t::RESET_UNSPECIFIED_AXES) || + coords.length > HB_VAR_COMPOSITE_MAX_AXES) + component_coords = hb_array (c.font->coords, c.font->num_coords); + + // Transform + + uint32_t transformVarIdx = VarIdx::NO_VARIATION; + if (flags & (unsigned) flags_t::TRANSFORM_HAS_VARIATION) + READ_UINT32VAR (transformVarIdx); + +#define PROCESS_TRANSFORM_COMPONENTS \ + HB_STMT_START { \ + PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_X, translateX); \ + PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_Y, translateY); \ + PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_ROTATION, rotation); \ + PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_X, scaleX); \ + PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_Y, scaleY); \ + PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_X, skewX); \ + PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_Y, skewY); \ + PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_X, tCenterX); \ + PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_Y, tCenterY); \ + } HB_STMT_END + + hb_transform_decomposed_t<> transform; + + // Read transform components +#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ + if (flags & (unsigned) flags_t::flag) \ + { \ + static_assert (type::static_size == HBINT16::static_size, ""); \ + if (unlikely (unsigned (end - record) < HBINT16::static_size)) \ + return hb_ubytes_t (); \ + hb_barrier (); \ + transform.name = mult * * (const HBINT16 *) record; \ + record += HBINT16::static_size; \ + } + PROCESS_TRANSFORM_COMPONENTS; +#undef PROCESS_TRANSFORM_COMPONENT + + // Read reserved records + unsigned i = flags & (unsigned) flags_t::RESERVED_MASK; + while (i) + { + HB_UNUSED uint32_t discard; + READ_UINT32VAR (discard); + i &= i - 1; + } + + /* Parsing is over now. */ + + if (show) + { + // Only use coord_setter if there's actually any axis overrides. + coord_setter_t coord_setter (axisIndices ? component_coords : hb_array ()); + // Go backwards, to reduce coord_setter vector reallocations. + for (unsigned i = axisIndices.length; i; i--) + coord_setter[axisIndices[i - 1]] = axisValues[i - 1]; + if (axisIndices) + component_coords = coord_setter.get_coords (); + + // Apply transform variations if any + if (transformVarIdx != VarIdx::NO_VARIATION && coords) + { + float transformValues[9]; + unsigned numTransformValues = 0; +#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ + if (flags & (unsigned) flags_t::flag) \ + transformValues[numTransformValues++] = transform.name / mult; + PROCESS_TRANSFORM_COMPONENTS; +#undef PROCESS_TRANSFORM_COMPONENT + varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache); + numTransformValues = 0; +#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ + if (flags & (unsigned) flags_t::flag) \ + transform.name = transformValues[numTransformValues++] * mult; + PROCESS_TRANSFORM_COMPONENTS; +#undef PROCESS_TRANSFORM_COMPONENT + } + + // Divide them by their divisors +#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ + if (flags & (unsigned) flags_t::flag) \ + { \ + HBINT16 int_v; \ + int_v = roundf (transform.name); \ + type typed_v = * (const type *) &int_v; \ + float float_v = (float) typed_v; \ + transform.name = float_v; \ + } + PROCESS_TRANSFORM_COMPONENTS; +#undef PROCESS_TRANSFORM_COMPONENT + + if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y)) + transform.scaleY = transform.scaleX; + + total_transform.transform (transform.to_transform ()); + total_transform.scale (c.font->x_mult ? 1.f / c.font->x_multf : 0.f, + c.font->y_mult ? 1.f / c.font->y_multf : 0.f); + + bool same_coords = component_coords.length == coords.length && + component_coords.arrayZ == coords.arrayZ; + + c.depth_left--; + VARC.get_path_at (c, gid, + component_coords, total_transform, + parent_gid, + same_coords ? cache : nullptr); + c.depth_left++; + } + +#undef PROCESS_TRANSFORM_COMPONENTS +#undef READ_UINT32VAR + + return hb_ubytes_t (record, end - record); +} + +bool +VARC::get_path_at (const hb_varc_context_t &c, + hb_codepoint_t glyph, + hb_array_t coords, + hb_transform_t<> transform, + hb_codepoint_t parent_glyph, + hb_scalar_cache_t *parent_cache) const +{ + // Don't recurse on the same glyph. + unsigned idx = glyph == parent_glyph ? + NOT_COVERED : + (this+coverage).get_coverage (glyph); + if (idx == NOT_COVERED) + { + if (c.draw_session) + { + // Build a transforming pen to apply the transform. + hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs (); + hb_transforming_pen_context_t context {transform, + c.draw_session->funcs, + c.draw_session->draw_data, + &c.draw_session->st}; + hb_draw_session_t transformer_session {transformer_funcs, &context}; + hb_draw_session_t &shape_draw_session = transform.is_identity () ? *c.draw_session : transformer_session; + + if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true; +#ifndef HB_NO_CFF + if (c.font->face->table.cff2->get_path_at (c.font, glyph, shape_draw_session, coords)) return true; + if (c.font->face->table.cff1->get_path (c.font, glyph, shape_draw_session)) return true; // Doesn't have variations +#endif + return false; + } + else if (c.extents) + { + hb_glyph_extents_t glyph_extents; + if (!c.font->face->table.glyf->get_extents_at (c.font, glyph, &glyph_extents, coords)) +#ifndef HB_NO_CFF + if (!c.font->face->table.cff2->get_extents_at (c.font, glyph, &glyph_extents, coords)) + if (!c.font->face->table.cff1->get_extents (c.font, glyph, &glyph_extents)) // Doesn't have variations +#endif + return false; + + hb_extents_t<> comp_extents (glyph_extents); + transform.transform_extents (comp_extents); + c.extents->union_ (comp_extents); + } + return true; + } + + if (c.depth_left <= 0) + return true; + + if (c.edges_left <= 0) + return true; + (c.edges_left)--; + + hb_decycler_node_t node (c.decycler); + if (unlikely (!node.visit (glyph))) + return true; + + hb_ubytes_t record = (this+glyphRecords)[idx]; + + hb_scalar_cache_t static_cache; + hb_scalar_cache_t *cache = parent_cache ? + parent_cache : + (this+varStore).create_cache (&static_cache); + + transform.scale (c.font->x_multf, c.font->y_multf); + + VarCompositeGlyph::get_path_at (c, + glyph, + coords, transform, + record, + cache); + + if (cache != parent_cache) + (this+varStore).destroy_cache (cache, &static_cache); + + return true; +} + +#endif + +//} // namespace Var +} // namespace OT + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh index 2ea1b6bca32..6b40f044556 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/Var/VARC/VARC.hh @@ -32,7 +32,7 @@ struct hb_varc_context_t { hb_font_t *font; hb_draw_session_t *draw_session; - hb_extents_t *extents; + hb_extents_t<> *extents; mutable hb_decycler_t decycler; mutable signed edges_left; mutable signed depth_left; @@ -65,9 +65,9 @@ struct VarComponent get_path_at (const hb_varc_context_t &c, hb_codepoint_t parent_gid, hb_array_t coords, - hb_transform_t transform, + hb_transform_t<> transform, hb_ubytes_t record, - VarRegionList::cache_t *cache = nullptr) const; + hb_scalar_cache_t *cache = nullptr) const; }; struct VarCompositeGlyph @@ -76,9 +76,9 @@ struct VarCompositeGlyph get_path_at (const hb_varc_context_t &c, hb_codepoint_t gid, hb_array_t coords, - hb_transform_t transform, + hb_transform_t<> transform, hb_ubytes_t record, - VarRegionList::cache_t *cache) + hb_scalar_cache_t *cache) { while (record) { @@ -104,9 +104,9 @@ struct VARC get_path_at (const hb_varc_context_t &c, hb_codepoint_t gid, hb_array_t coords, - hb_transform_t transform = HB_TRANSFORM_IDENTITY, + hb_transform_t<> transform = HB_TRANSFORM_IDENTITY, hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID, - VarRegionList::cache_t *parent_cache = nullptr) const; + hb_scalar_cache_t *parent_cache = nullptr) const; bool get_path (hb_font_t *font, @@ -129,7 +129,7 @@ struct VARC bool get_extents (hb_font_t *font, hb_codepoint_t gid, - hb_extents_t *extents, + hb_extents_t<> *extents, hb_varc_scratch_t &scratch) const { hb_varc_context_t c {font, @@ -194,9 +194,10 @@ struct VARC hb_codepoint_t gid, hb_glyph_extents_t *extents) const { +#ifndef HB_NO_DRAW if (!table->has_data ()) return false; - hb_extents_t f_extents; + hb_extents_t<> f_extents; auto *scratch = acquire_scratch (); if (unlikely (!scratch)) return true; @@ -207,6 +208,9 @@ struct VARC *extents = f_extents.to_glyph_extents (font->x_scale < 0, font->y_scale < 0); return ret; +#else + return false; +#endif } private: diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh index 1805df262aa..8f5287e9457 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/Glyph.hh @@ -102,17 +102,15 @@ struct Glyph if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); { + // Duplicated code. int lsb = 0; - int h_delta = face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? - (int) header->xMin - lsb : 0; + face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb); + int h_delta = (int) header->xMin - lsb; HB_UNUSED int tsb = 0; - int v_orig = (int) header->yMax + #ifndef HB_NO_VERTICAL - ((void) face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) -#else - 0 + face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb); #endif - ; + int v_orig = (int) header->yMax + tsb; unsigned h_adv = face->table.hmtx->get_advance_without_var_unscaled (gid); unsigned v_adv = #ifndef HB_NO_VERTICAL @@ -314,6 +312,7 @@ struct Glyph bool use_my_metrics = true, bool phantom_only = false, hb_array_t coords = hb_array_t (), + hb_scalar_cache_t *gvar_cache = nullptr, unsigned int depth = 0, unsigned *edge_count = nullptr) const { @@ -328,7 +327,7 @@ struct Glyph head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth); } - if (!coords) + if (!coords && font->has_nonzero_coords) coords = hb_array (font->coords, font->num_coords); contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points; @@ -357,17 +356,15 @@ struct Glyph if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; hb_array_t phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); { + // Duplicated code. int lsb = 0; - int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? - (int) header->xMin - lsb : 0; + glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb); + int h_delta = (int) header->xMin - lsb; HB_UNUSED int tsb = 0; - int v_orig = (int) header->yMax + #ifndef HB_NO_VERTICAL - ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) -#else - 0 + glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb); #endif - ; + int v_orig = (int) header->yMax + tsb; unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid); unsigned v_adv = #ifndef HB_NO_VERTICAL @@ -383,7 +380,7 @@ struct Glyph } #ifndef HB_NO_VAR - if (coords) + if (hb_any (coords)) { #ifndef HB_NO_BEYOND_64K if (glyf_accelerator.GVAR->has_data ()) @@ -391,6 +388,7 @@ struct Glyph coords, points.as_array ().sub_array (old_length), scratch, + gvar_cache, phantom_only && type == SIMPLE); else #endif @@ -398,6 +396,7 @@ struct Glyph coords, points.as_array ().sub_array (old_length), scratch, + gvar_cache, phantom_only && type == SIMPLE); } #endif @@ -447,6 +446,7 @@ struct Glyph use_my_metrics, phantom_only, coords, + gvar_cache, depth + 1, edge_count))) { @@ -533,7 +533,11 @@ struct Glyph bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator, hb_glyph_extents_t *extents) const { - if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + if (type == EMPTY) + { + *extents = {0, 0, 0, 0}; + return true; /* Empty glyph; zero extents. */ + } return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents); } diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh index 601e1303792..507c94f7f3d 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/SimpleGlyph.hh @@ -189,7 +189,7 @@ struct SimpleGlyph unsigned old_length = points.length; points.alloc (points.length + num_points + 4); // Allocate for phantom points, to avoid a possible copy - if (unlikely (!points.resize (points.length + num_points, false))) return false; + if (unlikely (!points.resize_dirty (points.length + num_points))) return false; auto points_ = points.as_array ().sub_array (old_length); if (!phantom_only) hb_memset (points_.arrayZ, 0, sizeof (contour_point_t) * num_points); diff --git a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh index d9e5fedfa92..3fe2506bec9 100644 --- a/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh +++ b/src/java.desktop/share/native/libharfbuzz/OT/glyf/glyf.hh @@ -220,7 +220,8 @@ struct glyf_accelerator_t template bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer, hb_array_t coords, - hb_glyf_scratch_t &scratch) const + hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr) const { if (gid >= num_glyphs) return false; @@ -228,7 +229,7 @@ struct glyf_accelerator_t all_points.resize (0); bool phantom_only = !consumer.is_consuming_contour_points (); - if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords))) + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords, gvar_cache))) return false; unsigned count = all_points.length; @@ -371,28 +372,28 @@ struct glyf_accelerator_t contour_point_t *get_phantoms_sink () { return phantoms; } }; +#ifndef HB_NO_VAR unsigned - get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + get_advance_with_var_unscaled (hb_codepoint_t gid, + hb_font_t *font, + bool is_vertical, + hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr) const { if (unlikely (gid >= num_glyphs)) return 0; bool success = false; contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; - if (font->num_coords) - { - hb_glyf_scratch_t scratch; - success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false), - hb_array (font->coords, font->num_coords), - scratch); - } - + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false), + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0), + scratch, gvar_cache); if (unlikely (!success)) - return -#ifndef HB_NO_VERTICAL - is_vertical ? vmtx->get_advance_without_var_unscaled (gid) : -#endif - hmtx->get_advance_without_var_unscaled (gid); + { + unsigned upem = font->face->get_upem (); + return is_vertical ? upem : upem / 2; + } float result = is_vertical ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y @@ -400,40 +401,38 @@ struct glyf_accelerator_t return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); } - bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const + float + get_v_origin_with_var_unscaled (hb_codepoint_t gid, + hb_font_t *font, + hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr) const { - if (unlikely (gid >= num_glyphs)) return false; + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; - hb_glyph_extents_t extents; - hb_glyf_scratch_t scratch; contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; - if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false), - hb_array (font->coords, font->num_coords), - scratch))) - return false; + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false), + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0), + scratch, gvar_cache); + if (unlikely (!success)) + { + return font->face->get_upem (); + } - *lsb = is_vertical - ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing - : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x); - return true; + return phantoms[glyf_impl::PHANTOM_TOP].y; } #endif - - bool get_leading_bearing_without_var_unscaled (hb_codepoint_t gid, bool is_vertical, int *lsb) const - { - if (unlikely (gid >= num_glyphs)) return false; - if (is_vertical) return false; // TODO Humm, what to do here? - - *lsb = glyph_for_gid (gid).get_header ()->xMin; - return true; - } +#endif public: bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const - { return get_extents_at (font, gid, extents, hb_array (font->coords, font->num_coords)); } + { return get_extents_at (font, gid, extents, hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0)); } bool get_extents_at (hb_font_t *font, hb_codepoint_t gid, @@ -445,12 +444,15 @@ struct glyf_accelerator_t #ifndef HB_NO_VAR if (coords) { - hb_glyf_scratch_t scratch; - return get_points (font, - gid, - points_aggregator_t (font, extents, nullptr, true), - coords, - scratch); + hb_glyf_scratch_t *scratch = acquire_scratch (); + if (unlikely (!scratch)) return false; + bool ret = get_points (font, + gid, + points_aggregator_t (font, extents, nullptr, true), + coords, + *scratch); + release_scratch (scratch); + return ret; } #endif return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents); @@ -485,33 +487,20 @@ struct glyf_accelerator_t } bool - get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const + get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, hb_scalar_cache_t *gvar_cache = nullptr) const { if (!has_data ()) return false; - hb_glyf_scratch_t *scratch; - - // Borrow the cached strach buffer. - { - scratch = cached_scratch.get_acquire (); - if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr))) - { - scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t)); - if (unlikely (!scratch)) - return true; - } - } + hb_glyf_scratch_t *scratch = acquire_scratch (); + if (unlikely (!scratch)) return true; bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session), - hb_array (font->coords, font->num_coords), - *scratch); + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0), + *scratch, + gvar_cache); - // Put it back. - if (!cached_scratch.cmpexch (nullptr, scratch)) - { - scratch->~hb_glyf_scratch_t (); - hb_free (scratch); - } + release_scratch (scratch); return ret; } @@ -519,12 +508,38 @@ struct glyf_accelerator_t bool get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, hb_array_t coords, - hb_glyf_scratch_t &scratch) const + hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr) const { if (!has_data ()) return false; return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session), coords, - scratch); + scratch, + gvar_cache); + } + + + hb_glyf_scratch_t *acquire_scratch () const + { + if (!has_data ()) return nullptr; + hb_glyf_scratch_t *scratch = cached_scratch.get_acquire (); + if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr))) + { + scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t)); + if (unlikely (!scratch)) + return nullptr; + } + return scratch; + } + void release_scratch (hb_glyf_scratch_t *scratch) const + { + if (!scratch) + return; + if (!cached_scratch.cmpexch (nullptr, scratch)) + { + scratch->~hb_glyf_scratch_t (); + hb_free (scratch); + } } #ifndef HB_NO_VAR diff --git a/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh index da6378820bb..d1f38b9de8a 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/classdef-graph.hh @@ -74,7 +74,7 @@ struct ClassDef : public OT::ClassDef class_def_link->width = SmallTypes::size; class_def_link->objidx = class_def_prime_id; class_def_link->position = link_position; - class_def_prime_vertex.add_parent (parent_id); + class_def_prime_vertex.add_parent (parent_id, false); return true; } @@ -117,7 +117,7 @@ struct ClassDef : public OT::ClassDef int64_t vertex_len = vertex.obj.tail - vertex.obj.head; if (vertex_len < OT::ClassDef::min_size) return false; hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: return ((ClassDefFormat1*)this)->sanitize (vertex); case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); diff --git a/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh index 61ca063e345..46c703524d9 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/coverage-graph.hh @@ -32,29 +32,27 @@ namespace graph { -struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3 -{ - bool sanitize (graph_t::vertex_t& vertex) const - { - int64_t vertex_len = vertex.obj.tail - vertex.obj.head; - constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3::min_size; - if (vertex_len < min_size) return false; - hb_barrier (); - return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size (); - } -}; +static bool sanitize ( + const OT::Layout::Common::CoverageFormat1_3* thiz, + graph_t::vertex_t& vertex +) { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3::min_size; + if (vertex_len < min_size) return false; + hb_barrier (); + return vertex_len >= min_size + thiz->glyphArray.get_size () - thiz->glyphArray.len.get_size (); +} -struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4 -{ - bool sanitize (graph_t::vertex_t& vertex) const - { - int64_t vertex_len = vertex.obj.tail - vertex.obj.head; - constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4::min_size; - if (vertex_len < min_size) return false; - hb_barrier (); - return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); - } -}; +static bool sanitize ( + const OT::Layout::Common::CoverageFormat2_4* thiz, + graph_t::vertex_t& vertex +) { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4::min_size; + if (vertex_len < min_size) return false; + hb_barrier (); + return vertex_len >= min_size + thiz->rangeRecord.get_size () - thiz->rangeRecord.len.get_size (); +} struct Coverage : public OT::Layout::Common::Coverage { @@ -98,11 +96,33 @@ struct Coverage : public OT::Layout::Common::Coverage coverage_link->width = SmallTypes::size; coverage_link->objidx = coverage_prime_id; coverage_link->position = link_position; - coverage_prime_vertex.add_parent (parent_id); + coverage_prime_vertex.add_parent (parent_id, false); return (Coverage*) coverage_prime_vertex.obj.head; } + // Filter an existing coverage table to glyphs at indices [start, end) and replace it with the filtered version. + static bool filter_coverage (gsubgpos_graph_context_t& c, + unsigned existing_coverage, + unsigned start, unsigned end) { + unsigned coverage_size = c.graph.vertices_[existing_coverage].table_size (); + auto& coverage_v = c.graph.vertices_[existing_coverage]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return false; + + auto new_coverage = + + hb_zip (coverage_table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second >= start && p.second < end; + }) + | hb_map_retains_sorting (hb_first) + ; + + return make_coverage (c, new_coverage, existing_coverage, coverage_size * 2 + 100); + } + + // Replace the coverage table at dest obj with one covering 'glyphs'. template static bool make_coverage (gsubgpos_graph_context_t& c, It glyphs, @@ -141,10 +161,10 @@ struct Coverage : public OT::Layout::Common::Coverage int64_t vertex_len = vertex.obj.tail - vertex.obj.head; if (vertex_len < OT::Layout::Common::Coverage::min_size) return false; hb_barrier (); - switch (u.format) + switch (u.format.v) { - case 1: return ((CoverageFormat1*)this)->sanitize (vertex); - case 2: return ((CoverageFormat2*)this)->sanitize (vertex); + case 1: return graph::sanitize ((const OT::Layout::Common::CoverageFormat1_3*) this, vertex); + case 2: return graph::sanitize ((const OT::Layout::Common::CoverageFormat2_4*) this, vertex); #ifndef HB_NO_BEYOND_64K // Not currently supported case 3: diff --git a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh index ed1026f5866..78ae1a9dd5b 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/graph.hh @@ -50,6 +50,7 @@ struct graph_t private: unsigned incoming_edges_ = 0; unsigned single_parent = (unsigned) -1; + bool has_incoming_virtual_edges_ = false; hb_hashmap_t parents; public: @@ -66,6 +67,11 @@ struct graph_t return parents.in_error (); } + bool has_incoming_virtual_edges () const + { + return has_incoming_virtual_edges_; + } + bool link_positions_valid (unsigned num_objects, bool removed_nil) { hb_set_t assigned_bytes; @@ -121,7 +127,9 @@ struct graph_t } } - bool equals (const vertex_t& other, + bool equals (unsigned this_index, + unsigned other_index, + const vertex_t& other, const graph_t& graph, const graph_t& other_graph, unsigned depth) const @@ -129,8 +137,10 @@ struct graph_t if (!(as_bytes () == other.as_bytes ())) { DEBUG_MSG (SUBSET_REPACK, nullptr, - "vertex [%lu] bytes != [%lu] bytes, depth = %u", + "vertex %u [%lu bytes] != %u [%lu bytes], depth = %u", + this_index, (unsigned long) table_size (), + other_index, (unsigned long) other.table_size (), depth); @@ -162,6 +172,7 @@ struct graph_t hb_swap (a.single_parent, b.single_parent); hb_swap (a.parents, b.parents); hb_swap (a.incoming_edges_, b.incoming_edges_); + hb_swap (a.has_incoming_virtual_edges_, b.has_incoming_virtual_edges_); hb_swap (a.start, b.start); hb_swap (a.end, b.end); hb_swap (a.priority, b.priority); @@ -207,13 +218,16 @@ struct graph_t void reset_parents () { incoming_edges_ = 0; + has_incoming_virtual_edges_ = false; single_parent = (unsigned) -1; parents.reset (); } - void add_parent (unsigned parent_index) + void add_parent (unsigned parent_index, bool is_virtual) { assert (parent_index != (unsigned) -1); + has_incoming_virtual_edges_ |= is_virtual; + if (incoming_edges_ == 0) { single_parent = parent_index; @@ -408,7 +422,7 @@ struct graph_t link_a.bias != link_b.bias) return false; - if (!graph.vertices_[link_a.objidx].equals ( + if (!graph.vertices_[link_a.objidx].equals (link_a.objidx, link_b.objidx, other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1)) return false; @@ -456,8 +470,12 @@ struct graph_t num_roots_for_space_.push (1); bool removed_nil = false; vertices_.alloc (objects.length); - vertices_scratch_.alloc (objects.length); + ordering_.resize (objects.length); + ordering_scratch_.alloc (objects.length); + unsigned count = objects.length; + unsigned order = objects.length; + unsigned skip = 0; for (unsigned i = 0; i < count; i++) { // If this graph came from a serialization buffer object 0 is the @@ -465,6 +483,9 @@ struct graph_t if (i == 0 && !objects.arrayZ[i]) { removed_nil = true; + order--; + ordering_.resize(objects.length - 1); + skip++; continue; } @@ -474,6 +495,12 @@ struct graph_t check_success (v->link_positions_valid (count, removed_nil)); + // To start we set the ordering to match the provided objects + // list. Note: objects are provided to us in reverse order (ie. + // the last object is the root). + unsigned obj_idx = i - skip; + ordering_[--order] = obj_idx; + if (!removed_nil) continue; // Fix indices to account for removed nil object. for (auto& l : v->obj.all_links_writer ()) { @@ -490,17 +517,20 @@ struct graph_t bool operator== (const graph_t& other) const { - return root ().equals (other.root (), *this, other, 0); + return root ().equals (root_idx(), other.root_idx(), other.root (), *this, other, 0); } void print () const { - for (int i = vertices_.length - 1; i >= 0; i--) + for (unsigned id : ordering_) { - const auto& v = vertices_[i]; - printf("%d: %u [", i, (unsigned int)v.table_size()); + const auto& v = vertices_[id]; + printf("%u: %u [", id, (unsigned int)v.table_size()); for (const auto &l : v.obj.real_links) { printf("%u, ", l.objidx); } + for (const auto &l : v.obj.virtual_links) { + printf("v%u, ", l.objidx); + } printf("]\n"); } } @@ -516,6 +546,7 @@ struct graph_t { return !successful || vertices_.in_error () || + ordering_.in_error() || num_roots_for_space_.in_error (); } @@ -526,10 +557,10 @@ struct graph_t unsigned root_idx () const { - // Object graphs are in reverse order, the first object is at the end - // of the vector. Since the graph is topologically sorted it's safe to + // First element of ordering_ is the root. + // Since the graph is topologically sorted it's safe to // assume the first object has no incoming edges. - return vertices_.length - 1; + return ordering_[0]; } const hb_serialize_context_t::object_t& object (unsigned i) const @@ -556,7 +587,7 @@ struct graph_t link->width = 2; link->objidx = child_id; link->position = (char*) offset - (char*) v.obj.head; - vertices_[child_id].add_parent (parent_id); + vertices_[child_id].add_parent (parent_id, false); } /* @@ -587,55 +618,51 @@ struct graph_t hb_priority_queue_t queue; queue.alloc (vertices_.length); - hb_vector_t &sorted_graph = vertices_scratch_; - if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; - hb_vector_t id_map; - if (unlikely (!check_success (id_map.resize (vertices_.length)))) return; + hb_vector_t &new_ordering = ordering_scratch_; + if (unlikely (!check_success (new_ordering.resize (vertices_.length)))) return; hb_vector_t removed_edges; if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return; update_parents (); queue.insert (root ().modified_distance (0), root_idx ()); - int new_id = root_idx (); unsigned order = 1; + unsigned pos = 0; while (!queue.in_error () && !queue.is_empty ()) { unsigned next_id = queue.pop_minimum().second; - sorted_graph[new_id] = std::move (vertices_[next_id]); - const vertex_t& next = sorted_graph[new_id]; - - if (unlikely (!check_success(new_id >= 0))) { + if (unlikely (!check_success(pos < new_ordering.length))) { // We are out of ids. Which means we've visited a node more than once. // This graph contains a cycle which is not allowed. DEBUG_MSG (SUBSET_REPACK, nullptr, "Invalid graph. Contains cycle."); return; } - - id_map[next_id] = new_id--; + new_ordering[pos++] = next_id; + const vertex_t& next = vertices_[next_id]; for (const auto& link : next.obj.all_links ()) { removed_edges[link.objidx]++; - if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx])) + const auto& v = vertices_[link.objidx]; + if (!(v.incoming_edges () - removed_edges[link.objidx])) // Add the order that the links were encountered to the priority. // This ensures that ties between priorities objects are broken in a consistent // way. More specifically this is set up so that if a set of objects have the same // distance they'll be added to the topological order in the order that they are // referenced from the parent object. - queue.insert (vertices_[link.objidx].modified_distance (order++), + queue.insert (v.modified_distance (order++), link.objidx); } } check_success (!queue.in_error ()); - check_success (!sorted_graph.in_error ()); + check_success (!new_ordering.in_error ()); - check_success (remap_all_obj_indices (id_map, &sorted_graph)); - vertices_ = std::move (sorted_graph); + hb_swap (ordering_, new_ordering); - if (!check_success (new_id == -1)) + if (!check_success (pos == vertices_.length)) { print_orphaned_nodes (); + } } /* @@ -645,8 +672,8 @@ struct graph_t */ void find_space_roots (hb_set_t& visited, hb_set_t& roots) { - int root_index = (int) root_idx (); - for (int i = root_index; i >= 0; i--) + unsigned root_index = root_idx (); + for (unsigned i : ordering_) { if (visited.has (i)) continue; @@ -829,7 +856,6 @@ struct graph_t if (subgraph.in_error ()) return false; - unsigned original_root_idx = root_idx (); hb_map_t index_map; bool made_changes = false; for (auto entry : subgraph.iter ()) @@ -852,14 +878,6 @@ struct graph_t if (!made_changes) return false; - if (original_root_idx != root_idx () - && parents.has (original_root_idx)) - { - // If the root idx has changed since parents was determined, update root idx in parents - parents.add (root_idx ()); - parents.del (original_root_idx); - } - auto new_subgraph = + subgraph.keys () | hb_map([&] (uint32_t node_idx) { @@ -943,12 +961,14 @@ struct graph_t /* * Moves the child of old_parent_idx pointed to by old_offset to a new * vertex at the new_offset. + * + * Returns the id of the child node that was moved. */ template - void move_child (unsigned old_parent_idx, - const O* old_offset, - unsigned new_parent_idx, - const O* new_offset) + unsigned move_child (unsigned old_parent_idx, + const O* old_offset, + unsigned new_parent_idx, + const O* new_offset) { distance_invalid = true; positions_invalid = true; @@ -965,10 +985,56 @@ struct graph_t new_link->position = (const char*) new_offset - (const char*) new_v.obj.head; auto& child = vertices_[child_id]; - child.add_parent (new_parent_idx); + child.add_parent (new_parent_idx, false); old_v.remove_real_link (child_id, old_offset); child.remove_parent (old_parent_idx); + + return child_id; + } + + /* + * Moves all outgoing links in old parent that have + * a link position between [old_post_start, old_pos_end) + * to the new parent. Links are placed serially in the new + * parent starting at new_pos_start. + */ + template + void move_children (unsigned old_parent_idx, + unsigned old_pos_start, + unsigned old_pos_end, + unsigned new_parent_idx, + unsigned new_pos_start) + { + distance_invalid = true; + positions_invalid = true; + + auto& old_v = vertices_[old_parent_idx]; + auto& new_v = vertices_[new_parent_idx]; + + hb_vector_t old_links; + for (const auto& l : old_v.obj.real_links) + { + if (l.position < old_pos_start || l.position >= old_pos_end) + { + old_links.push(l); + continue; + } + + unsigned array_pos = l.position - old_pos_start; + + unsigned child_id = l.objidx; + auto* new_link = new_v.obj.real_links.push (); + new_link->width = O::static_size; + new_link->objidx = child_id; + new_link->position = new_pos_start + array_pos; + + auto& child = vertices_[child_id]; + child.add_parent (new_parent_idx, false); + child.remove_parent (old_parent_idx); + } + + old_v.obj.real_links = std::move (old_links); } /* @@ -1000,8 +1066,11 @@ struct graph_t distance_invalid = true; auto* clone = vertices_.push (); + unsigned clone_idx = vertices_.length - 1; + ordering_.push(clone_idx); + auto& child = vertices_[node_idx]; - if (vertices_.in_error ()) { + if (vertices_.in_error () || ordering_.in_error()) { return -1; } @@ -1011,51 +1080,23 @@ struct graph_t clone->space = child.space; clone->reset_parents (); - unsigned clone_idx = vertices_.length - 2; for (const auto& l : child.obj.real_links) { clone->obj.real_links.push (l); - vertices_[l.objidx].add_parent (clone_idx); + vertices_[l.objidx].add_parent (clone_idx, false); } for (const auto& l : child.obj.virtual_links) { clone->obj.virtual_links.push (l); - vertices_[l.objidx].add_parent (clone_idx); + vertices_[l.objidx].add_parent (clone_idx, true); } check_success (!clone->obj.real_links.in_error ()); check_success (!clone->obj.virtual_links.in_error ()); - // The last object is the root of the graph, so swap back the root to the end. - // The root's obj idx does change, however since it's root nothing else refers to it. - // all other obj idx's will be unaffected. - hb_swap (vertices_[vertices_.length - 2], *clone); - - // Since the root moved, update the parents arrays of all children on the root. - for (const auto& l : root ().obj.all_links ()) - vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); - return clone_idx; } - /* - * Creates a copy of child and re-assigns the link from - * parent to the clone. The copy is a shallow copy, objects - * linked from child are not duplicated. - * - * Returns the index of the newly created duplicate. - * - * If the child_idx only has incoming edges from parent_idx, this - * will do nothing and return the original child_idx. - */ - unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) - { - unsigned new_idx = duplicate (parent_idx, child_idx); - if (new_idx == (unsigned) -1) return child_idx; - return new_idx; - } - - /* * Creates a copy of child and re-assigns the link from * parent to the clone. The copy is a shallow copy, objects @@ -1073,10 +1114,15 @@ struct graph_t const auto& child = vertices_[child_idx]; unsigned links_to_child = child.incoming_edges_from_parent(parent_idx); - if (child.incoming_edges () <= links_to_child) + if (child.incoming_edges () <= links_to_child || child.has_incoming_virtual_edges()) { // Can't duplicate this node, doing so would orphan the original one as all remaining links // to child are from parent. + // + // We don't allow duplication of nodes with incoming virtual edges because we don't track + // the number of virtual vs real incoming edges. As a result we can't tell if a node + // with virtual edges may end up orphaned by duplication (ie. where one copy is only pointed + // to by virtual edges). DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u => %u", parent_idx, child_idx); return -1; @@ -1091,12 +1137,15 @@ struct graph_t if (parent_idx == clone_idx) parent_idx++; auto& parent = vertices_[parent_idx]; + unsigned count = 0; + unsigned num_real = parent.obj.real_links.length; for (auto& l : parent.obj.all_links_writer ()) { + count++; if (l.objidx != child_idx) continue; - reassign_link (l, parent_idx, clone_idx); + reassign_link (l, parent_idx, clone_idx, count > num_real); } return clone_idx; @@ -1129,10 +1178,15 @@ struct graph_t links_to_child += child.incoming_edges_from_parent(parent_idx); } - if (child.incoming_edges () <= links_to_child) + if (child.incoming_edges () <= links_to_child || child.has_incoming_virtual_edges()) { // Can't duplicate this node, doing so would orphan the original one as all remaining links // to child are from parent. + // + // We don't allow duplication of nodes with incoming virtual edges because we don't track + // the number of virtual vs real incoming edges. As a result we can't tell if a node + // with virtual edges may end up orphaned by duplication (ie. where one copy is only pointed + // to by virtual edges). DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx); return -1; } @@ -1146,12 +1200,15 @@ struct graph_t // duplicate shifts the root node idx, so if parent_idx was root update it. if (parent_idx == clone_idx) parent_idx++; auto& parent = vertices_[parent_idx]; + unsigned count = 0; + unsigned num_real = parent.obj.real_links.length; for (auto& l : parent.obj.all_links_writer ()) { + count++; if (l.objidx != child_idx) continue; - reassign_link (l, parent_idx, clone_idx); + reassign_link (l, parent_idx, clone_idx, count > num_real); } } @@ -1168,7 +1225,10 @@ struct graph_t distance_invalid = true; auto* clone = vertices_.push (); - if (vertices_.in_error ()) { + unsigned clone_idx = vertices_.length - 1; + ordering_.push(clone_idx); + + if (vertices_.in_error () || ordering_.in_error()) { return -1; } @@ -1177,20 +1237,37 @@ struct graph_t clone->distance = 0; clone->space = 0; - unsigned clone_idx = vertices_.length - 2; - - // The last object is the root of the graph, so swap back the root to the end. - // The root's obj idx does change, however since it's root nothing else refers to it. - // all other obj idx's will be unaffected. - hb_swap (vertices_[vertices_.length - 2], *clone); - - // Since the root moved, update the parents arrays of all children on the root. - for (const auto& l : root ().obj.all_links ()) - vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); - return clone_idx; } + /* + * Creates a new child node and remap the old child to it. + * + * Returns the index of the newly created child. + * + */ + unsigned remap_child (unsigned parent_idx, unsigned old_child_idx) + { + unsigned new_child_idx = duplicate (old_child_idx); + if (new_child_idx == (unsigned) -1) return -1; + + auto& parent = vertices_[parent_idx]; + for (auto& l : parent.obj.real_links) + { + if (l.objidx != old_child_idx) + continue; + reassign_link (l, parent_idx, new_child_idx, false); + } + + for (auto& l : parent.obj.virtual_links) + { + if (l.objidx != old_child_idx) + continue; + reassign_link (l, parent_idx, new_child_idx, true); + } + return new_child_idx; + } + /* * Raises the sorting priority of all children. */ @@ -1279,6 +1356,7 @@ struct graph_t if (!DEBUG_ENABLED(SUBSET_REPACK)) return; DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected."); + parents_invalid = true; update_parents(); @@ -1348,7 +1426,8 @@ struct graph_t size_t total_size = 0; unsigned count = vertices_.length; for (unsigned i = 0; i < count; i++) { - size_t size = vertices_.arrayZ[i].obj.tail - vertices_.arrayZ[i].obj.head; + const auto& obj = vertices_.arrayZ[i].obj; + size_t size = obj.tail - obj.head; total_size += size; } return total_size; @@ -1398,8 +1477,11 @@ struct graph_t for (unsigned p = 0; p < count; p++) { - for (auto& l : vertices_.arrayZ[p].obj.all_links ()) - vertices_[l.objidx].add_parent (p); + for (auto& l : vertices_.arrayZ[p].obj.real_links) + vertices_[l.objidx].add_parent (p, false); + + for (auto& l : vertices_.arrayZ[p].obj.virtual_links) + vertices_[l.objidx].add_parent (p, true); } for (unsigned i = 0; i < count; i++) @@ -1418,7 +1500,7 @@ struct graph_t if (!positions_invalid) return; unsigned current_pos = 0; - for (int i = root_idx (); i >= 0; i--) + for (unsigned i : ordering_) { auto& v = vertices_[i]; v.start = current_pos; @@ -1450,11 +1532,11 @@ struct graph_t unsigned count = vertices_.length; for (unsigned i = 0; i < count; i++) vertices_.arrayZ[i].distance = hb_int_max (int64_t); - vertices_.tail ().distance = 0; + vertices_[root_idx ()].distance = 0; hb_priority_queue_t queue; queue.alloc (count); - queue.insert (0, vertices_.length - 1); + queue.insert (0, root_idx ()); hb_vector_t visited; visited.resize (vertices_.length); @@ -1464,22 +1546,23 @@ struct graph_t unsigned next_idx = queue.pop_minimum ().second; if (visited[next_idx]) continue; const auto& next = vertices_[next_idx]; - int64_t next_distance = vertices_[next_idx].distance; + int64_t next_distance = next.distance; visited[next_idx] = true; for (const auto& link : next.obj.all_links ()) { if (visited[link.objidx]) continue; - const auto& child = vertices_.arrayZ[link.objidx].obj; + auto& child_v = vertices_.arrayZ[link.objidx]; + const auto& child = child_v.obj; unsigned link_width = link.width ? link.width : 4; // treat virtual offsets as 32 bits wide int64_t child_weight = (child.tail - child.head) + - ((int64_t) 1 << (link_width * 8)) * (vertices_.arrayZ[link.objidx].space + 1); + ((int64_t) 1 << (link_width * 8)) * (child_v.space + 1); int64_t child_distance = next_distance + child_weight; - if (child_distance < vertices_.arrayZ[link.objidx].distance) + if (child_distance < child_v.distance) { - vertices_.arrayZ[link.objidx].distance = child_distance; + child_v.distance = child_distance; queue.insert (child_distance, link.objidx); } } @@ -1502,12 +1585,13 @@ struct graph_t */ void reassign_link (hb_serialize_context_t::object_t::link_t& link, unsigned parent_idx, - unsigned new_idx) + unsigned new_idx, + bool is_virtual) { unsigned old_idx = link.objidx; link.objidx = new_idx; vertices_[old_idx].remove_parent (parent_idx); - vertices_[new_idx].add_parent (parent_idx); + vertices_[new_idx].add_parent (parent_idx, is_virtual); } /* @@ -1521,36 +1605,21 @@ struct graph_t if (!id_map) return; for (unsigned i : subgraph) { - for (auto& link : vertices_[i].obj.all_links_writer ()) + auto& obj = vertices_[i].obj; + unsigned num_real = obj.real_links.length; + unsigned count = 0; + for (auto& link : obj.all_links_writer ()) { + count++; const uint32_t *v; if (!id_map.has (link.objidx, &v)) continue; - if (only_wide && !(link.width == 4 && !link.is_signed)) continue; + if (only_wide && (link.is_signed || (link.width != 4 && link.width != 3))) continue; - reassign_link (link, i, *v); + reassign_link (link, i, *v, count > num_real); } } } - /* - * Updates all objidx's in all links using the provided mapping. - */ - bool remap_all_obj_indices (const hb_vector_t& id_map, - hb_vector_t* sorted_graph) const - { - unsigned count = sorted_graph->length; - for (unsigned i = 0; i < count; i++) - { - if (!(*sorted_graph)[i].remap_parents (id_map)) - return false; - for (auto& link : sorted_graph->arrayZ[i].obj.all_links_writer ()) - { - link.objidx = id_map[link.objidx]; - } - } - return true; - } - /* * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped. * For this search the graph is treated as being undirected. @@ -1586,7 +1655,16 @@ struct graph_t public: // TODO(garretrieger): make private, will need to move most of offset overflow code into graph. hb_vector_t vertices_; - hb_vector_t vertices_scratch_; + + // Specifies the current topological ordering of this graph + // + // ordering_[pos] = obj index + // + // specifies that the 'pos'th spot is filled by the object + // given by obj index. + hb_vector_t ordering_; + hb_vector_t ordering_scratch_; + private: bool parents_invalid; bool distance_invalid; diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh index b25d538fe3d..8969d007879 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-context.hh @@ -41,6 +41,7 @@ struct gsubgpos_graph_context_t unsigned lookup_list_index; hb_hashmap_t lookups; hb_hashmap_t subtable_to_extension; + hb_hashmap_t> split_subtables; HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_, graph_t& graph_); diff --git a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh index 7ad2ba430b7..ea6bcd239a0 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/gsubgpos-graph.hh @@ -27,9 +27,11 @@ #include "graph.hh" #include "../hb-ot-layout-gsubgpos.hh" #include "../OT/Layout/GSUB/ExtensionSubst.hh" +#include "../OT/Layout/GSUB/SubstLookupSubTable.hh" #include "gsubgpos-context.hh" #include "pairpos-graph.hh" #include "markbasepos-graph.hh" +#include "ligature-graph.hh" #ifndef GRAPH_GSUBGPOS_GRAPH_HH #define GRAPH_GSUBGPOS_GRAPH_HH @@ -85,6 +87,12 @@ struct Lookup : public OT::Lookup return lookupType == extension_type (table_tag); } + bool use_mark_filtering_set () const + { + unsigned flag = lookupFlag; + return flag & 0x0010u; + } + bool make_extension (gsubgpos_graph_context_t& c, unsigned this_index) { @@ -120,22 +128,18 @@ struct Lookup : public OT::Lookup unsigned type = lookupType; bool is_ext = is_extension (c.table_tag); - if (c.table_tag != HB_OT_TAG_GPOS) + if (c.table_tag != HB_OT_TAG_GPOS && c.table_tag != HB_OT_TAG_GSUB) return true; - if (!is_ext && - type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair && - type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + if (!is_ext && !is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c)) return true; hb_vector_t>> all_new_subtables; for (unsigned i = 0; i < subTable.len; i++) { unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); - unsigned parent_index = this_index; if (is_ext) { unsigned ext_subtable_index = subtable_index; - parent_index = ext_subtable_index; ExtensionFormat1* extension = (ExtensionFormat1*) c.graph.object (ext_subtable_index).head; @@ -144,26 +148,47 @@ struct Lookup : public OT::Lookup subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index); type = extension->get_lookup_type (); - if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair - && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + if (!is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c)) continue; } - hb_vector_t new_sub_tables; - switch (type) + hb_vector_t* split_result; + if (c.split_subtables.has (subtable_index, &split_result)) { - case 2: - new_sub_tables = split_subtable (c, parent_index, subtable_index); break; - case 4: - new_sub_tables = split_subtable (c, parent_index, subtable_index); break; - default: - break; + if (split_result->length == 0) + continue; + all_new_subtables.push (hb_pair(i, *split_result)); + } + else + { + hb_vector_t new_sub_tables; + + if (c.table_tag == HB_OT_TAG_GPOS) { + switch (type) + { + case 2: + new_sub_tables = split_subtable (c, subtable_index); break; + case 4: + new_sub_tables = split_subtable (c, subtable_index); break; + default: + break; + } + } else if (c.table_tag == HB_OT_TAG_GSUB) { + switch (type) + { + case 4: + new_sub_tables = split_subtable (c, subtable_index); break; + default: + break; + } + } + + if (new_sub_tables.in_error ()) return false; + + c.split_subtables.set (subtable_index, new_sub_tables); + if (new_sub_tables) + all_new_subtables.push (hb_pair (i, std::move (new_sub_tables))); } - if (new_sub_tables.in_error ()) return false; - if (!new_sub_tables) continue; - hb_pair_t>* entry = all_new_subtables.push (); - entry->first = i; - entry->second = std::move (new_sub_tables); } if (all_new_subtables) { @@ -175,30 +200,29 @@ struct Lookup : public OT::Lookup template hb_vector_t split_subtable (gsubgpos_graph_context_t& c, - unsigned parent_idx, unsigned objidx) { T* sub_table = (T*) c.graph.object (objidx).head; if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx])) return hb_vector_t (); - return sub_table->split_subtables (c, parent_idx, objidx); + return sub_table->split_subtables (c, objidx); } bool add_sub_tables (gsubgpos_graph_context_t& c, unsigned this_index, unsigned type, - hb_vector_t>>& subtable_ids) + const hb_vector_t>>& subtable_ids) { bool is_ext = is_extension (c.table_tag); - auto& v = c.graph.vertices_[this_index]; + auto* v = &c.graph.vertices_[this_index]; fix_existing_subtable_links (c, this_index, subtable_ids); unsigned new_subtable_count = 0; for (const auto& p : subtable_ids) new_subtable_count += p.second.length; - size_t new_size = v.table_size () + size_t new_size = v->table_size () + new_subtable_count * OT::Offset16::static_size; char* buffer = (char*) hb_calloc (1, new_size); if (!buffer) return false; @@ -207,10 +231,13 @@ struct Lookup : public OT::Lookup hb_free (buffer); return false; } - hb_memcpy (buffer, v.obj.head, v.table_size()); + hb_memcpy (buffer, v->obj.head, v->table_size()); - v.obj.head = buffer; - v.obj.tail = buffer + new_size; + if (use_mark_filtering_set ()) + hb_memcpy (buffer + new_size - 2, v->obj.tail - 2, 2); + + v->obj.head = buffer; + v->obj.tail = buffer + new_size; Lookup* new_lookup = (Lookup*) buffer; @@ -226,21 +253,23 @@ struct Lookup : public OT::Lookup if (is_ext) { unsigned ext_id = create_extension_subtable (c, subtable_id, type); - c.graph.vertices_[subtable_id].add_parent (ext_id); + c.graph.vertices_[subtable_id].add_parent (ext_id, false); subtable_id = ext_id; + // the reference to v may have changed on adding a node, so reassign it. + v = &c.graph.vertices_[this_index]; } - auto* link = v.obj.real_links.push (); + auto* link = v->obj.real_links.push (); link->width = 2; link->objidx = subtable_id; link->position = (char*) &new_lookup->subTable[offset_index++] - (char*) new_lookup; - c.graph.vertices_[subtable_id].add_parent (this_index); + c.graph.vertices_[subtable_id].add_parent (this_index, false); } } // Repacker sort order depends on link order, which we've messed up so resort it. - v.obj.real_links.qsort (); + v->obj.real_links.qsort (); // The head location of the lookup has changed, invalidating the lookups map entry // in the context. Update the map. @@ -250,17 +279,15 @@ struct Lookup : public OT::Lookup void fix_existing_subtable_links (gsubgpos_graph_context_t& c, unsigned this_index, - hb_vector_t>>& subtable_ids) + const hb_vector_t>>& subtable_ids) { auto& v = c.graph.vertices_[this_index]; - Lookup* lookup = (Lookup*) v.obj.head; - unsigned shift = 0; for (const auto& p : subtable_ids) { unsigned insert_index = p.first + shift; unsigned pos_offset = p.second.length * OT::Offset16::static_size; - unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup; + unsigned insert_offset = Lookup::min_size + insert_index * OT::Offset16::static_size; shift += p.second.length; for (auto& l : v.obj.all_links_writer ()) @@ -326,7 +353,7 @@ struct Lookup : public OT::Lookup // Make extension point at the subtable. auto& ext_vertex = c.graph.vertices_[ext_index]; - ext_vertex.add_parent (lookup_index); + ext_vertex.add_parent (lookup_index, false); if (!existing_ext_index) subtable_vertex.remap_parent (lookup_index, ext_index); @@ -334,6 +361,19 @@ struct Lookup : public OT::Lookup } private: + bool is_supported_gsub_type(unsigned type, gsubgpos_graph_context_t& c) const { + return (c.table_tag == HB_OT_TAG_GSUB) && ( + type == OT::Layout::GSUB_impl::SubstLookupSubTable::Type::Ligature + ); + } + + bool is_supported_gpos_type(unsigned type, gsubgpos_graph_context_t& c) const { + return (c.table_tag == HB_OT_TAG_GPOS) && ( + type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair || + type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase + ); + } + unsigned extension_type (hb_tag_t table_tag) const { switch (table_tag) diff --git a/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh index fb4166128a9..6d1a3d4ae4b 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/markbasepos-graph.hh @@ -212,7 +212,6 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { hb_set_t visited; @@ -265,7 +264,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { - switch (u.format) { + switch (u.format.v) { case 1: - return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, this_index); #ifndef HB_NO_BEYOND_64K case 2: HB_FALLTHROUGH; // Don't split 24bit MarkBasePos's. @@ -496,10 +494,10 @@ struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; - if (vertex_len < u.format.get_size ()) return false; + if (vertex_len < u.format.v.get_size ()) return false; hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); #ifndef HB_NO_BEYOND_64K diff --git a/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh index fd46861de46..85950b63457 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/pairpos-graph.hh @@ -49,7 +49,6 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { hb_set_t visited; @@ -84,7 +83,7 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3 (split_context, split_points); @@ -207,7 +206,6 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; @@ -291,7 +289,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4width = SmallTypes::size; class_def_link->objidx = class_def_2_id; class_def_link->position = 10; - graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id); + graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id, false); graph.duplicate (pair_pos_prime_id, class_def_2_id); return pair_pos_prime_id; @@ -607,14 +605,13 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { - switch (u.format) { + switch (u.format.v) { case 1: - return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + return ((PairPosFormat1*)(&u.format1))->split_subtables (c, this_index); case 2: - return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); + return ((PairPosFormat2*)(&u.format2))->split_subtables (c, this_index); #ifndef HB_NO_BEYOND_64K case 3: HB_FALLTHROUGH; case 4: HB_FALLTHROUGH; @@ -628,10 +625,10 @@ struct PairPos : public OT::Layout::GPOS_impl::PairPos bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; - if (vertex_len < u.format.get_size ()) return false; + if (vertex_len < u.format.v.get_size ()) return false; hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); case 2: diff --git a/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh b/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh index 06e4bf44d8e..37fac8909e5 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/serialize.hh @@ -113,7 +113,7 @@ will_overflow (graph_t& graph, hb_hashmap_t record_set; const auto& vertices = graph.vertices_; - for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--) + for (unsigned parent_idx : graph.ordering_) { // Don't need to check virtual links for overflow for (const auto& link : vertices.arrayZ[parent_idx].obj.real_links) @@ -172,14 +172,16 @@ void print_overflows (graph_t& graph, template inline void serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, char* head, + unsigned size, + const hb_vector_t& id_map, hb_serialize_context_t* c) { + assert(link.position + link.width <= size); + OT::Offset* offset = reinterpret_cast*> (head + link.position); *offset = 0; c->add_link (*offset, - // serializer has an extra nil object at the start of the - // object array. So all id's are +1 of what our id's are. - link.objidx + 1, + id_map[link.objidx], (hb_serialize_context_t::whence_t) link.whence, link.bias); } @@ -187,6 +189,8 @@ serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, inline void serialize_link (const hb_serialize_context_t::object_t::link_t& link, char* head, + unsigned size, + const hb_vector_t& id_map, hb_serialize_context_t* c) { switch (link.width) @@ -197,21 +201,21 @@ void serialize_link (const hb_serialize_context_t::object_t::link_t& link, case 4: if (link.is_signed) { - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); } else { - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); } return; case 2: if (link.is_signed) { - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); } else { - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); } return; case 3: - serialize_link_of_type (link, head, c); + serialize_link_of_type (link, head, size, id_map, c); return; default: // Unexpected link width. @@ -237,25 +241,36 @@ inline hb_blob_t* serialize (const graph_t& graph) c.start_serialize (); const auto& vertices = graph.vertices_; - for (unsigned i = 0; i < vertices.length; i++) { + + // Objects are placed in the serializer in reverse order since children need + // to be inserted before their parents. + + // Maps from our obj id's to the id's used during this serialization. + hb_vector_t id_map; + id_map.resize(graph.ordering_.length); + for (int pos = graph.ordering_.length - 1; pos >= 0; pos--) { + unsigned i = graph.ordering_[pos]; c.push (); - size_t size = vertices[i].obj.tail - vertices[i].obj.head; + auto& v = vertices[i]; + + size_t size = v.obj.tail - v.obj.head; + char* start = c.allocate_size (size); if (!start) { DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space."); return nullptr; } - hb_memcpy (start, vertices[i].obj.head, size); + hb_memcpy (start, v.obj.head, size); // Only real links needs to be serialized. - for (const auto& link : vertices[i].obj.real_links) - serialize_link (link, start, &c); + for (const auto& link : v.obj.real_links) + serialize_link (link, start, size, id_map, &c); // All duplications are already encoded in the graph, so don't // enable sharing during packing. - c.pop_pack (false); + id_map[i] = c.pop_pack (false); } c.end_serialize (); diff --git a/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh b/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh index 61fd7c2d2ff..f1f86e332db 100644 --- a/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh +++ b/src/java.desktop/share/native/libharfbuzz/graph/split-helpers.hh @@ -49,7 +49,7 @@ hb_vector_t actuate_subtable_split (Context& split_context, if (id == (unsigned) -1) { new_objects.reset (); - new_objects.allocated = -1; // mark error + new_objects.ensure_error (); return new_objects; } new_objects.push (id); @@ -58,7 +58,7 @@ hb_vector_t actuate_subtable_split (Context& split_context, if (!split_context.shrink (split_points[0])) { new_objects.reset (); - new_objects.allocated = -1; // mark error + new_objects.ensure_error (); } return new_objects; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh index d5a44bf473c..d2ce32616be 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh @@ -47,8 +47,7 @@ using namespace OT; struct ankr; -using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>; -static_assert (sizeof (hb_aat_class_cache_t) == 256, ""); +using hb_aat_class_cache_t = hb_ot_layout_mapping_cache_t; struct hb_aat_scratch_t { @@ -79,7 +78,10 @@ struct hb_aat_scratch_t { hb_bit_set_t *s = buffer_glyph_set.get_acquire (); if (s && buffer_glyph_set.cmpexch (s, nullptr)) + { + s->clear (); return s; + } s = (hb_bit_set_t *) hb_calloc (1, sizeof (hb_bit_set_t)); if (unlikely (!s)) @@ -124,13 +126,14 @@ struct hb_aat_apply_context_t : const OT::GDEF &gdef; bool has_glyph_classes; const hb_sorted_vector_t *range_flags = nullptr; + hb_mask_t subtable_flags = 0; + bool buffer_is_reversed = false; + // Caches bool using_buffer_glyph_set = false; hb_bit_set_t *buffer_glyph_set = nullptr; - const hb_bit_set_t *left_set = nullptr; - const hb_bit_set_t *right_set = nullptr; - const hb_bit_set_t *machine_glyph_set = nullptr; + const hb_bit_set_t *first_set = nullptr; + const hb_bit_set_t *second_set = nullptr; hb_aat_class_cache_t *machine_class_cache = nullptr; - hb_mask_t subtable_flags = 0; /* Unused. For debug tracing only. */ unsigned int lookup_index; @@ -146,6 +149,12 @@ struct hb_aat_apply_context_t : void set_lookup_index (unsigned int i) { lookup_index = i; } + void reverse_buffer () + { + buffer->reverse (); + buffer_is_reversed = !buffer_is_reversed; + } + void setup_buffer_glyph_set () { using_buffer_glyph_set = buffer->len >= 4 && buffer_glyph_set; @@ -156,11 +165,11 @@ struct hb_aat_apply_context_t : bool buffer_intersects_machine () const { if (likely (using_buffer_glyph_set)) - return buffer_glyph_set->intersects (*machine_glyph_set); + return buffer_glyph_set->intersects (*first_set); // Faster for shorter buffers. for (unsigned i = 0; i < buffer->len; i++) - if (machine_glyph_set->has (buffer->info[i].codepoint)) + if (first_set->has (buffer->info[i].codepoint)) return true; return false; } @@ -639,6 +648,23 @@ struct LookupFormat10 glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1); } + template + void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const + { + if (unlikely (!glyphCount)) return; + if (firstGlyph == DELETED_GLYPH) return; + const HBUINT8 *p = valueArrayZ.arrayZ; + for (unsigned i = 0; i < glyphCount; i++) + { + unsigned int v = 0; + unsigned int count = valueSize; + for (unsigned int j = 0; j < count; j++) + v = (v << 8) | *p++; + if (filter (v)) + glyphs.add (firstGlyph + i); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -666,7 +692,7 @@ struct Lookup { const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_value (glyph_id, num_glyphs); case 2: hb_barrier (); return u.format2.get_value (glyph_id); case 4: hb_barrier (); return u.format4.get_value (glyph_id); @@ -678,7 +704,7 @@ struct Lookup const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { /* Format 10 cannot return a pointer. */ case 10: hb_barrier (); return u.format10.get_value_or_null (glyph_id); default: @@ -690,7 +716,7 @@ struct Lookup template void collect_glyphs (set_t &glyphs, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0.collect_glyphs (glyphs, num_glyphs); return; case 2: hb_barrier (); u.format2.collect_glyphs (glyphs); return; case 4: hb_barrier (); u.format4.collect_glyphs (glyphs); return; @@ -703,12 +729,13 @@ struct Lookup template void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0.collect_glyphs_filtered (glyphs, num_glyphs, filter); return; case 2: hb_barrier (); u.format2.collect_glyphs_filtered (glyphs, filter); return; case 4: hb_barrier (); u.format4.collect_glyphs_filtered (glyphs, filter); return; case 6: hb_barrier (); u.format6.collect_glyphs_filtered (glyphs, filter); return; case 8: hb_barrier (); u.format8.collect_glyphs_filtered (glyphs, filter); return; + case 10: hb_barrier (); u.format10.collect_glyphs_filtered (glyphs, filter); return; default:return; } } @@ -724,9 +751,9 @@ struct Lookup bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 4: hb_barrier (); return_trace (u.format4.sanitize (c)); @@ -739,9 +766,9 @@ struct Lookup bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c, base)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c, base)); case 4: hb_barrier (); return_trace (u.format4.sanitize (c, base)); @@ -754,7 +781,7 @@ struct Lookup protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ LookupFormat0 format0; LookupFormat2 format2; LookupFormat4 format4; @@ -763,7 +790,7 @@ struct Lookup LookupFormat10 format10; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2); @@ -838,11 +865,6 @@ struct StateTable STATE_START_OF_LINE = 1, }; - template - void collect_glyphs (set_t &glyphs, unsigned num_glyphs) const - { - (this+classTable).collect_glyphs (glyphs, num_glyphs); - } template void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs, const table_t &table) const { @@ -1082,6 +1104,8 @@ struct SubtableGlyphCoverage for (unsigned i = 0; i < subtable_count; i++) { uint32_t offset = (uint32_t) subtableOffsets[i]; + // A font file called SFNSDisplay.ttf has value 0xFFFFFFFF in the offsets. + // Just ignore it. if (offset == 0 || offset == 0xFFFFFFFF) continue; if (unlikely (!subtableOffsets[i].sanitize (c, this, bytes))) @@ -1192,11 +1216,24 @@ struct StateTableDriver int state = StateTableT::STATE_START_OF_TEXT; // If there's only one range, we already checked the flag. auto *last_range = ac->range_flags && (ac->range_flags->length > 1) ? &(*ac->range_flags)[0] : nullptr; + const bool start_state_safe_to_break_eot = + !c->table->is_actionable (machine.get_entry (StateTableT::STATE_START_OF_TEXT, CLASS_END_OF_TEXT)); for (buffer->idx = 0; buffer->successful;) { - /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ - if (last_range) + unsigned int klass = likely (buffer->idx < buffer->len) ? + machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : + (unsigned) CLASS_END_OF_TEXT; + resume: + DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); + const EntryT &entry = machine.get_entry (state, klass); + const int next_state = machine.new_state (entry.newState); + + bool is_not_epsilon_transition = !(entry.flags & Flags::DontAdvance); + bool is_not_actionable = !c->table->is_actionable (entry); + + if (unlikely (last_range)) { + /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ auto *range = last_range; if (buffer->idx < buffer->len) { @@ -1211,7 +1248,7 @@ struct StateTableDriver } if (!(range->flags & ac->subtable_flags)) { - if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + if (buffer->idx == buffer->len) break; state = StateTableT::STATE_START_OF_TEXT; @@ -1219,13 +1256,42 @@ struct StateTableDriver continue; } } + else + { + // Fast path for when transitioning from start-state to start-state with + // no action and advancing. Do so as long as the class remains the same. + // This is common with runs of non-actionable glyphs. - unsigned int klass = likely (buffer->idx < buffer->len) ? - machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : - (unsigned) CLASS_END_OF_TEXT; - DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); - const EntryT &entry = machine.get_entry (state, klass); - const int next_state = machine.new_state (entry.newState); + bool is_null_transition = state == StateTableT::STATE_START_OF_TEXT && + next_state == StateTableT::STATE_START_OF_TEXT && + start_state_safe_to_break_eot && + is_not_actionable && + is_not_epsilon_transition && + !last_range; + + if (is_null_transition) + { + unsigned old_klass = klass; + do + { + c->transition (buffer, this, entry); + + if (buffer->idx == buffer->len || !buffer->successful) + break; + + (void) buffer->next_glyph (); + + klass = likely (buffer->idx < buffer->len) ? + machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : + (unsigned) CLASS_END_OF_TEXT; + } while (klass == old_klass); + + if (buffer->idx == buffer->len || !buffer->successful) + break; + + goto resume; + } + } /* Conditions under which it's guaranteed safe-to-break before current glyph: * @@ -1292,10 +1358,10 @@ struct StateTableDriver state = next_state; DEBUG_MSG (APPLY, nullptr, "s%d", state); - if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + if (buffer->idx == buffer->len) break; - if (!(entry.flags & Flags::DontAdvance) || buffer->max_ops-- <= 0) + if (is_not_epsilon_transition || buffer->max_ops-- <= 0) (void) buffer->next_glyph (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh index ca4f2243346..2a6b813972f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh @@ -120,12 +120,12 @@ struct KerxSubTableFormat0 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { for (const KernPair& pair : pairs) { - left_set.add (pair.left); - right_set.add (pair.right); + first_set.add (pair.left); + second_set.add (pair.right); } } @@ -140,7 +140,7 @@ struct KerxSubTableFormat0 int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { - if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0; + if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0; return table.get_kerning (left, right, c); } }; @@ -396,10 +396,10 @@ struct KerxSubTableFormat1 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { - machine.collect_initial_glyphs (left_set, num_glyphs, *this); - //machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning + machine.collect_initial_glyphs (first_set, num_glyphs, *this); + //machine.collect_glyphs (second_set, num_glyphs); // second_set is unused for machine kerning } protected: @@ -451,10 +451,10 @@ struct KerxSubTableFormat2 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { - (this+leftClassTable).collect_glyphs (left_set, num_glyphs); - (this+rightClassTable).collect_glyphs (right_set, num_glyphs); + (this+leftClassTable).collect_glyphs (first_set, num_glyphs); + (this+rightClassTable).collect_glyphs (second_set, num_glyphs); } struct accelerator_t @@ -468,7 +468,7 @@ struct KerxSubTableFormat2 int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { - if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0; + if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0; return table.get_kerning (left, right, c); } }; @@ -629,6 +629,8 @@ struct KerxSubTableFormat4 } o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK; o.attach_chain() = (int) mark - (int) buffer->idx; + if (c->buffer_is_reversed) + o.attach_chain() = -o.attach_chain(); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; } @@ -671,10 +673,10 @@ struct KerxSubTableFormat4 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { - machine.collect_initial_glyphs (left_set, num_glyphs, *this); - //machine.collect_glyphs (right_set, num_glyphs); // right_set is unused for machine kerning + machine.collect_initial_glyphs (first_set, num_glyphs, *this); + //machine.collect_glyphs (second_set, num_glyphs); // second_set is unused for machine kerning } protected: @@ -762,19 +764,19 @@ struct KerxSubTableFormat6 } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { if (is_long ()) { const auto &t = u.l; - (this+t.rowIndexTable).collect_glyphs (left_set, num_glyphs); - (this+t.columnIndexTable).collect_glyphs (right_set, num_glyphs); + (this+t.rowIndexTable).collect_glyphs (first_set, num_glyphs); + (this+t.columnIndexTable).collect_glyphs (second_set, num_glyphs); } else { const auto &t = u.s; - (this+t.rowIndexTable).collect_glyphs (left_set, num_glyphs); - (this+t.columnIndexTable).collect_glyphs (right_set, num_glyphs); + (this+t.rowIndexTable).collect_glyphs (first_set, num_glyphs); + (this+t.columnIndexTable).collect_glyphs (second_set, num_glyphs); } } @@ -789,7 +791,7 @@ struct KerxSubTableFormat6 int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { - if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0; + if (!(*c->first_set)[left] || !(*c->second_set)[right]) return 0; return table.get_kerning (left, right, c); } }; @@ -878,15 +880,15 @@ struct KerxSubTable } template - void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const + void collect_glyphs (set_t &first_set, set_t &second_set, unsigned num_glyphs) const { unsigned int subtable_type = get_type (); switch (subtable_type) { - case 0: u.format0.collect_glyphs (left_set, right_set, num_glyphs); return; - case 1: u.format1.collect_glyphs (left_set, right_set, num_glyphs); return; - case 2: u.format2.collect_glyphs (left_set, right_set, num_glyphs); return; - case 4: u.format4.collect_glyphs (left_set, right_set, num_glyphs); return; - case 6: u.format6.collect_glyphs (left_set, right_set, num_glyphs); return; + case 0: u.format0.collect_glyphs (first_set, second_set, num_glyphs); return; + case 1: u.format1.collect_glyphs (first_set, second_set, num_glyphs); return; + case 2: u.format2.collect_glyphs (first_set, second_set, num_glyphs); return; + case 4: u.format4.collect_glyphs (first_set, second_set, num_glyphs); return; + case 6: u.format6.collect_glyphs (first_set, second_set, num_glyphs); return; default: return; } } @@ -923,8 +925,8 @@ struct KerxSubTable struct kern_subtable_accelerator_data_t { - hb_bit_set_t left_set; - hb_bit_set_t right_set; + hb_bit_set_t first_set; + hb_bit_set_t second_set; mutable hb_aat_class_cache_t class_cache; }; @@ -1017,9 +1019,8 @@ struct KerxTable if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ()) goto skip; - c->left_set = &subtable_accel.left_set; - c->right_set = &subtable_accel.right_set; - c->machine_glyph_set = &subtable_accel.left_set; + c->first_set = &subtable_accel.first_set; + c->second_set = &subtable_accel.second_set; c->machine_class_cache = &subtable_accel.class_cache; if (!c->buffer_intersects_machine ()) @@ -1051,8 +1052,8 @@ struct KerxTable } } - if (reverse) - c->buffer->reverse (); + if (reverse != c->buffer_is_reversed) + c->reverse_buffer (); { /* See comment in sanitize() for conditional here. */ @@ -1060,15 +1061,14 @@ struct KerxTable ret |= st->dispatch (c); } - if (reverse) - c->buffer->reverse (); - (void) c->buffer->message (c->font, "end subtable %u", c->lookup_index); skip: st = &StructAfter (*st); c->set_lookup_index (c->lookup_index + 1); } + if (c->buffer_is_reversed) + c->reverse_buffer (); return ret; } @@ -1133,7 +1133,7 @@ struct KerxTable if (unlikely (accel_data.subtable_accels.in_error ())) return accel_data; - st->collect_glyphs (subtable_accel.left_set, subtable_accel.right_set, num_glyphs); + st->collect_glyphs (subtable_accel.first_set, subtable_accel.second_set, num_glyphs); subtable_accel.class_cache.clear (); st = &StructAfter (*st); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh index 92b07eec6e5..2cbb86c7566 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh @@ -487,7 +487,7 @@ struct LigatureSubtable if (entry.flags & LigatureEntryT::SetComponent) { /* Never mark same index twice, in case DontAdvance was used... */ - if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) + if (unlikely (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len)) match_length--; match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; @@ -640,7 +640,7 @@ struct NoncontextualSubtable for (unsigned int i = 0; i < count; i++) { /* This block copied from StateTableDriver::drive. Keep in sync. */ - if (last_range) + if (unlikely (last_range)) { auto *range = last_range; { @@ -1169,15 +1169,15 @@ struct Chain hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); }))) goto skip; - c->subtable_flags = subtable_flags; - c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t); - c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr; - if (!(coverage & ChainSubtable::AllDirections) && HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != bool (coverage & ChainSubtable::Vertical)) goto skip; + c->subtable_flags = subtable_flags; + c->first_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t); + c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr; + if (!c->buffer_intersects_machine ()) { (void) c->buffer->message (c->font, "skipped chainsubtable %u because no glyph matches", c->lookup_index); @@ -1219,22 +1219,21 @@ struct Chain if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index)) goto skip; - if (reverse) - c->buffer->reverse (); + if (reverse != c->buffer_is_reversed) + c->reverse_buffer (); subtable->apply (c); - if (reverse) - c->buffer->reverse (); - (void) c->buffer->message (c->font, "end chainsubtable %u", c->lookup_index); - if (unlikely (!c->buffer->successful)) return; + if (unlikely (!c->buffer->successful)) break; skip: subtable = &StructAfter> (*subtable); c->set_lookup_index (c->lookup_index + 1); } + if (c->buffer_is_reversed) + c->reverse_buffer (); } unsigned int get_size () const { return length; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh index 6e250bfdef0..651ffcda01d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh @@ -78,129 +78,246 @@ /* - * Big-endian integers. + * Fixed-endian integers / floats. */ + /* Endian swap, used in Windows related backends */ static inline constexpr uint16_t hb_uint16_swap (uint16_t v) { return (v >> 8) | (v << 8); } static inline constexpr uint32_t hb_uint32_swap (uint32_t v) { return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } -#ifndef HB_FAST_INT_ACCESS +template +struct __attribute__((packed)) hb_packed_t { Type v; }; + +#ifndef HB_FAST_NUM_ACCESS + #if defined(__OPTIMIZE__) && \ defined(__BYTE_ORDER) && \ (__BYTE_ORDER == __BIG_ENDIAN || \ (__BYTE_ORDER == __LITTLE_ENDIAN && \ hb_has_builtin(__builtin_bswap16) && \ - hb_has_builtin(__builtin_bswap32))) -#define HB_FAST_INT_ACCESS 1 + hb_has_builtin(__builtin_bswap32) && \ + hb_has_builtin(__builtin_bswap64))) +#define HB_FAST_NUM_ACCESS 1 #else -#define HB_FAST_INT_ACCESS 0 -#endif +#define HB_FAST_NUM_ACCESS 0 #endif -template -struct BEInt; -template -struct BEInt +// https://github.com/harfbuzz/harfbuzz/issues/5456 +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ <= 12) +#undef HB_FAST_NUM_ACCESS +#define HB_FAST_NUM_ACCESS 0 +#endif + +#endif + +template +struct HBInt; +template +struct HBInt { public: - BEInt () = default; - constexpr BEInt (Type V) : v {uint8_t (V)} {} + HBInt () = default; + constexpr HBInt (Type V) : v {uint8_t (V)} {} constexpr operator Type () const { return v; } private: uint8_t v; }; -template -struct BEInt +template +struct HBInt { - struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; - public: - BEInt () = default; + HBInt () = default; - BEInt (Type V) -#if HB_FAST_INT_ACCESS -#if __BYTE_ORDER == __LITTLE_ENDIAN - { ((packed_uint16_t *) v)->v = __builtin_bswap16 (V); } -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - { ((packed_uint16_t *) v)->v = V; } -#endif + HBInt (Type V) +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + ((hb_packed_t *) v)->v = V; + else + ((hb_packed_t *) v)->v = __builtin_bswap16 (V); + } #else - : v {uint8_t ((V >> 8) & 0xFF), - uint8_t ((V ) & 0xFF)} {} + : v {BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V ) & 0xFF), + BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 8) & 0xFF)} {} #endif - constexpr operator Type () const { -#if HB_FAST_INT_ACCESS -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap16 (((packed_uint16_t *) v)->v); -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint16_t *) v)->v; -#endif + constexpr operator Type () const + { +#if HB_FAST_NUM_ACCESS + return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? + ((const hb_packed_t *) v)->v + : + __builtin_bswap16 (((const hb_packed_t *) v)->v) + ; #else - return (v[0] << 8) - + (v[1] ); + return (BE ? (v[0] << 8) : (v[0] )) + + (BE ? (v[1] ) : (v[1] << 8)); #endif } private: uint8_t v[2]; }; -template -struct BEInt +template +struct HBInt { static_assert (!std::is_signed::value, ""); public: - BEInt () = default; - constexpr BEInt (Type V) : v {uint8_t ((V >> 16) & 0xFF), - uint8_t ((V >> 8) & 0xFF), - uint8_t ((V ) & 0xFF)} {} + HBInt () = default; + constexpr HBInt (Type V) : v {BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 16) & 0xFF), + BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 8) & 0xFF), + BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V ) & 0xFF)} {} - constexpr operator Type () const { return (v[0] << 16) - + (v[1] << 8) - + (v[2] ); } + constexpr operator Type () const { return (BE ? (v[0] << 16) : (v[0] )) + + (BE ? (v[1] << 8) : (v[1] << 8)) + + (BE ? (v[2] ) : (v[2] << 16)); } private: uint8_t v[3]; }; -template -struct BEInt +template +struct HBInt { - struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + template + friend struct HBFloat; public: - BEInt () = default; + HBInt () = default; - BEInt (Type V) -#if HB_FAST_INT_ACCESS -#if __BYTE_ORDER == __LITTLE_ENDIAN - { ((packed_uint32_t *) v)->v = __builtin_bswap32 (V); } -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - { ((packed_uint32_t *) v)->v = V; } -#endif + HBInt (Type V) +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + ((hb_packed_t *) v)->v = V; + else + ((hb_packed_t *) v)->v = __builtin_bswap32 (V); + } #else - : v {uint8_t ((V >> 24) & 0xFF), - uint8_t ((V >> 16) & 0xFF), - uint8_t ((V >> 8) & 0xFF), - uint8_t ((V ) & 0xFF)} {} + : v {BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V ) & 0xFF), + BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 8) & 0xFF), + BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 16) & 0xFF), + BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 24) & 0xFF)} {} #endif constexpr operator Type () const { -#if HB_FAST_INT_ACCESS -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap32 (((packed_uint32_t *) v)->v); -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint32_t *) v)->v; -#endif +#if HB_FAST_NUM_ACCESS + return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? + ((const hb_packed_t *) v)->v + : + __builtin_bswap32 (((const hb_packed_t *) v)->v) + ; #else - return (v[0] << 24) - + (v[1] << 16) - + (v[2] << 8) - + (v[3] ); + return (BE ? (v[0] << 24) : (v[0] )) + + (BE ? (v[1] << 16) : (v[1] << 8)) + + (BE ? (v[2] << 8) : (v[2] << 16)) + + (BE ? (v[3] ) : (v[3] << 24)); #endif } private: uint8_t v[4]; }; +template +struct HBInt +{ + template + friend struct HBFloat; + + public: + HBInt () = default; + + HBInt (Type V) +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + ((hb_packed_t *) v)->v = V; + else + ((hb_packed_t *) v)->v = __builtin_bswap64 (V); + } +#else + : v {BE ? uint8_t ((V >> 56) & 0xFF) : uint8_t ((V ) & 0xFF), + BE ? uint8_t ((V >> 48) & 0xFF) : uint8_t ((V >> 8) & 0xFF), + BE ? uint8_t ((V >> 40) & 0xFF) : uint8_t ((V >> 16) & 0xFF), + BE ? uint8_t ((V >> 32) & 0xFF) : uint8_t ((V >> 24) & 0xFF), + BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V >> 32) & 0xFF), + BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 40) & 0xFF), + BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 48) & 0xFF), + BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 56) & 0xFF)} {} +#endif + + constexpr operator Type () const { +#if HB_FAST_NUM_ACCESS + return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? + ((const hb_packed_t *) v)->v + : + __builtin_bswap64 (((const hb_packed_t *) v)->v) + ; +#else + return (BE ? (uint64_t (v[0]) << 56) : (uint64_t (v[0]) )) + + (BE ? (uint64_t (v[1]) << 48) : (uint64_t (v[1]) << 8)) + + (BE ? (uint64_t (v[2]) << 40) : (uint64_t (v[2]) << 16)) + + (BE ? (uint64_t (v[3]) << 32) : (uint64_t (v[3]) << 24)) + + (BE ? (uint64_t (v[4]) << 24) : (uint64_t (v[4]) << 32)) + + (BE ? (uint64_t (v[5]) << 16) : (uint64_t (v[5]) << 40)) + + (BE ? (uint64_t (v[6]) << 8) : (uint64_t (v[6]) << 48)) + + (BE ? (uint64_t (v[7]) ) : (uint64_t (v[7]) << 56)); +#endif + } + private: uint8_t v[8]; +}; /* Floats. */ +template +struct HBFloat +{ + using IntType = typename std::conditional::type; + + public: + HBFloat () = default; + + HBFloat (Type V) + { +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + { + ((hb_packed_t *) v)->v = V; + return; + } + } +#endif + + union { + hb_packed_t f; + hb_packed_t i; + } u = {{V}}; + + const HBInt I = u.i.v; + for (unsigned i = 0; i < Bytes; i++) + v[i] = I.v[i]; + } + + /* c++14 constexpr */ operator Type () const + { +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + return ((const hb_packed_t *) v)->v; + } +#endif + + HBInt I; + for (unsigned i = 0; i < Bytes; i++) + I.v[i] = v[i]; + + union { + hb_packed_t i; + hb_packed_t f; + } u = {{I}}; + + return u.f.v; + } + private: uint8_t v[Bytes]; +}; + + /* We want our rounding towards +infinity. */ static inline double _hb_roundf (double x) { return floor (x + .5); } @@ -210,6 +327,27 @@ _hb_roundf (float x) { return floorf (x + .5f); } #define roundf(x) _hb_roundf(x) +static inline void +hb_sincos (float rotation, float &s, float &c) +{ +#ifdef HAVE_SINCOSF + sincosf (rotation, &s, &c); +#else + c = cosf (rotation); + s = sinf (rotation); +#endif +} +static inline void +hb_sincos (double rotation, double &s, double &c) +{ +#ifdef HAVE_SINCOS + sincos (rotation, &s, &c); +#else + c = cos (rotation); + s = sin (rotation); +#endif +} + /* Encodes three unsigned integers in one 64-bit number. If the inputs have more than 21 bits, * values will be truncated / overlap, and might not decode exactly. */ @@ -734,6 +872,17 @@ HB_FUNCOBJ (hb_clamp); * Bithacks. */ +/* Return the number of 1 bits in a uint8_t; faster than hb_popcount() */ +static inline unsigned +hb_popcount8 (uint8_t v) +{ + static const uint8_t popcount4[16] = { + 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 + }; + return popcount4[v & 0xF] + popcount4[v >> 4]; +} + /* Return the number of 1 bits in v. */ template static inline unsigned int @@ -1070,6 +1219,7 @@ _hb_cmp_operator (const void *pkey, const void *pval) } template +HB_HOT static inline bool hb_bsearch_impl (unsigned *pos, /* Out */ const K& key, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-alloc-pool.hh b/src/java.desktop/share/native/libharfbuzz/hb-alloc-pool.hh new file mode 100644 index 00000000000..cb0d7aa7acd --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-alloc-pool.hh @@ -0,0 +1,105 @@ +/* + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_ALLOC_POOL_HH +#define HB_ALLOC_POOL_HH + +#include "hb-vector.hh" + +/* Memory pool for persistent small- to medium-sized allocations. + * + * Some AI musings on this, not necessarily true: + * + * This is a very simple implementation, but it's good enough for our + * purposes. It's not thread-safe. It's not very fast. It's not + * very memory efficient. It's not very cache efficient. It's not + * very anything efficient. But it's simple and it works. And it's + * good enough for our purposes. If you need something more + * sophisticated, use a real allocator. Or use a real language. */ + +struct hb_alloc_pool_t +{ + unsigned ChunkSize = 65536 - 2 * sizeof (void *); + + void *alloc (size_t size, unsigned alignment = 2 * sizeof (void *)) + { + if (unlikely (chunks.in_error ())) return nullptr; + + assert (alignment > 0); + assert (alignment <= 2 * sizeof (void *)); + assert ((alignment & (alignment - 1)) == 0); /* power of two */ + + if (size > (ChunkSize) / 4) + { + /* Big chunk, allocate separately. */ + hb_vector_t chunk; + if (unlikely (!chunk.resize (size))) return nullptr; + void *ret = chunk.arrayZ; + chunks.push (std::move (chunk)); + if (chunks.in_error ()) return nullptr; + if (chunks.length > 1) + { + // Bring back the previous last chunk to the end, so that + // we can continue to allocate from it. + hb_swap (chunks.arrayZ[chunks.length - 1], chunks.arrayZ[chunks.length - 2]); + } + return ret; + } + + unsigned pad = (unsigned) ((alignment - ((uintptr_t) current_chunk.arrayZ & (alignment - 1))) & (alignment - 1)); + + // Small chunk, allocate from the last chunk. + if (current_chunk.length < pad + size) + { + chunks.push (); + if (unlikely (chunks.in_error ())) return nullptr; + hb_vector_t &chunk = chunks.arrayZ[chunks.length - 1]; + if (unlikely (!chunk.resize (ChunkSize))) return nullptr; + current_chunk = chunk; + pad = (unsigned) ((alignment - ((uintptr_t) current_chunk.arrayZ & (alignment - 1))) & (alignment - 1)); + } + + current_chunk += pad; + + assert (current_chunk.length >= size); + void *ret = current_chunk.arrayZ; + current_chunk += size; + return ret; + } + + void discard (void *p_, size_t size) + { + // Reclaim memory if we can. + char *p = (char *) p_; + if (current_chunk.arrayZ == p + size && current_chunk.backwards_length >= size) + current_chunk -= size; + } + + private: + hb_vector_t> chunks; + hb_array_t current_chunk; +}; + + +#endif /* HB_ALLOC_POOL_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-array.hh index d65bbc5c711..71d3bcdf1ea 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-array.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-array.hh @@ -291,6 +291,13 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> && (unsigned int) (arrayZ + length - (const char *) p) >= size; } + template + bool check_end (const void *p) const + { + return (uintptr_t) (((const char *) p) - arrayZ) <= length; + } + /* Only call if you allocated the underlying array using hb_malloc() or similar. */ void fini () { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh index 80bd90e87dc..05aaf5a155e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh @@ -40,7 +40,6 @@ * Atomic integers and pointers. */ - /* We need external help for these */ #if defined(hb_atomic_int_impl_add) \ @@ -80,27 +79,11 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) #include +#define HB_STL_ATOMIC_IMPL + #define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire) #define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release) -#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) -#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_relaxed)) -#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast::type> *> (AI)->store ((V), std::memory_order_release)) -#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_relaxed)) -#define hb_atomic_int_impl_get(AI) (reinterpret_cast::type> const *> (AI)->load (std::memory_order_acquire)) - -#define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed)) -#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast const *> (P)->load (std::memory_order_relaxed)) -#define hb_atomic_ptr_impl_get(P) (reinterpret_cast *> (P)->load (std::memory_order_acquire)) -static inline bool -_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) -{ - const void *O = O_; // Need lvalue - return reinterpret_cast *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed); -} -#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) - - #else /* defined(HB_NO_MT) */ #define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V)) @@ -159,6 +142,76 @@ inline T hb_atomic_int_impl_get (const T *AI) { T v = *AI; _hb_memory_r_barrie inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } #endif +#ifdef HB_STL_ATOMIC_IMPL +template +struct hb_atomic_t +{ + hb_atomic_t () = default; + constexpr hb_atomic_t (T v) : v (v) {} + constexpr hb_atomic_t (const hb_atomic_t& o) : v (o.get_relaxed ()) {} + constexpr hb_atomic_t (hb_atomic_t&& o) : v (o.get_relaxed ()) { o.set_relaxed ({}); } + + hb_atomic_t &operator= (const hb_atomic_t& o) { set_relaxed (o.get_relaxed ()); return *this; } + hb_atomic_t &operator= (hb_atomic_t&& o){ set_relaxed (o.get_relaxed ()); o.set_relaxed ({}); return *this; } + hb_atomic_t &operator= (T v_) + { + set_relaxed (v_); + return *this; + } + operator T () const { return get_relaxed (); } + + void set_relaxed (T v_) { v.store (v_, std::memory_order_relaxed); } + void set_release (T v_) { v.store (v_, std::memory_order_release); } + T get_relaxed () const { return v.load (std::memory_order_relaxed); } + T get_acquire () const { return v.load (std::memory_order_acquire); } + T inc () { return v.fetch_add (1, std::memory_order_acq_rel); } + T dec () { return v.fetch_add (-1, std::memory_order_acq_rel); } + + int operator++ (int) { return inc (); } + int operator-- (int) { return dec (); } + + friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept + { + T v = a.get_acquire (); + a.set_relaxed (b.get_acquire ()); + b.set_relaxed (v); + } + + std::atomic v = 0; +}; + +template +struct hb_atomic_t +{ + hb_atomic_t () = default; + constexpr hb_atomic_t (T *v) : v (v) {} + hb_atomic_t (const hb_atomic_t &other) = delete; + + void init (T *v_ = nullptr) { set_relaxed (v_); } + void set_relaxed (T *v_) { v.store (v_, std::memory_order_relaxed); } + T *get_relaxed () const { return v.load (std::memory_order_relaxed); } + T *get_acquire () const { return v.load (std::memory_order_acquire); } + bool cmpexch (T *old, T *new_) { return v.compare_exchange_weak (old, new_, std::memory_order_acq_rel, std::memory_order_relaxed); } + + operator bool () const { return get_acquire () != nullptr; } + T *operator->() const { return get_acquire (); } + template + operator C * () const + { + return get_acquire (); + } + + friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept + { + T *p = a.get_acquire (); + a.set_relaxed (b.get_acquire ()); + b.set_relaxed (p); + } + + std::atomic v = nullptr; +}; + +#else template struct hb_atomic_t @@ -178,7 +231,6 @@ struct hb_atomic_t int operator ++ (int) { return inc (); } int operator -- (int) { return dec (); } - long operator |= (long v_) { set_relaxed (get_relaxed () | v_); return *this; } T v = 0; }; @@ -194,7 +246,7 @@ struct hb_atomic_t void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } - bool cmpexch (const T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + bool cmpexch (T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } operator bool () const { return get_acquire () != nullptr; } T * operator -> () const { return get_acquire (); } @@ -203,6 +255,8 @@ struct hb_atomic_t T *v = nullptr; }; +#endif + static inline bool hb_barrier () { _hb_compiler_memory_r_barrier (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh index f541472544a..f9c0e8870ff 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh @@ -176,7 +176,7 @@ struct hb_inc_bimap_t { hb_codepoint_t count = get_population (); hb_vector_t work; - if (unlikely (!work.resize (count, false))) return; + if (unlikely (!work.resize_dirty (count))) return; for (hb_codepoint_t rhs = 0; rhs < count; rhs++) work.arrayZ[rhs] = back_map[rhs]; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh index 9562a9674a5..902db195507 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-page.hh @@ -142,6 +142,7 @@ struct hb_bit_page_t bool operator [] (hb_codepoint_t g) const { return get (g); } bool operator () (hb_codepoint_t g) const { return get (g); } + bool has (hb_codepoint_t g) const { return get (g); } void add_range (hb_codepoint_t a, hb_codepoint_t b) { @@ -290,7 +291,7 @@ struct hb_bit_page_t unsigned int j = m & ELT_MASK; const elt_t vv = v[i] & ~((elt_t (1) << j) - 1); - for (const elt_t *p = &vv; i < len (); p = &v[++i]) + for (const elt_t *p = &vv; i < len (); p = ((const elt_t *) &v[0]) + (++i)) if (*p) { *codepoint = i * ELT_BITS + elt_get_min (*p); @@ -346,6 +347,36 @@ struct hb_bit_page_t return 0; } + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_bit_page_t &s_ = Null (hb_bit_page_t), bool init = true) : s (&s_), v (INVALID) + { + if (init) + v = s->get_min (); + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { + s->next (&v); + } + void __prev__ () { s->previous (&v); } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return v != o.v; } + + protected: + const hb_bit_page_t *s; + hb_codepoint_t v; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } + static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; typedef unsigned long long elt_t; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh index 740a2437671..4028dd63531 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set-invertible.hh @@ -368,7 +368,7 @@ struct hb_bit_set_invertible_t unsigned __len__ () const { return l; } iter_t end () const { return iter_t (*s, false); } bool operator != (const iter_t& o) const - { return v != o.v || s != o.s; } + { return v != o.v; } protected: const hb_bit_set_invertible_t *s; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh index 799c3607599..f86d5750854 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-bit-set.hh @@ -91,10 +91,10 @@ struct hb_bit_set_t if (pages.length < count && (unsigned) pages.allocated < count && count <= 2) exact_size = true; // Most sets are small and local - if (unlikely (!pages.resize (count, clear, exact_size) || - !page_map.resize (count, clear))) + if (unlikely (!pages.resize_full (count, clear, exact_size) || + !page_map.resize_full (count, clear, false))) { - pages.resize (page_map.length, clear, exact_size); + pages.resize_full (page_map.length, clear, exact_size); successful = false; return false; } @@ -108,10 +108,11 @@ struct hb_bit_set_t page_map.alloc (sz); } - void reset () + hb_bit_set_t& reset () { successful = true; clear (); + return *this; } void clear () @@ -394,7 +395,7 @@ struct hb_bit_set_t { if (unlikely (!successful)) return; unsigned int count = other.pages.length; - if (unlikely (!resize (count, false, exact_size))) + if (unlikely (!resize (count, false, exact_size))) return; population = other.population; @@ -922,7 +923,7 @@ struct hb_bit_set_t unsigned __len__ () const { return l; } iter_t end () const { return iter_t (*s, false); } bool operator != (const iter_t& o) const - { return s != o.s || v != o.v; } + { return v != o.v; } protected: const hb_bit_set_t *s; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh index 9e21beebd34..87c1524df6f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-deserialize-text-unicode.hh @@ -32,7 +32,7 @@ #include "hb.hh" -#line 36 "hb-buffer-deserialize-text-unicode.hh" +#line 33 "hb-buffer-deserialize-text-unicode.hh" static const unsigned char _deserialize_text_unicode_trans_keys[] = { 0u, 0u, 43u, 102u, 48u, 102u, 48u, 124u, 48u, 57u, 62u, 124u, 48u, 124u, 60u, 117u, 85u, 117u, 85u, 117u, 0 @@ -150,12 +150,12 @@ _hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer, hb_glyph_info_t info = {0}; const hb_glyph_position_t pos = {0}; -#line 154 "hb-buffer-deserialize-text-unicode.hh" +#line 147 "hb-buffer-deserialize-text-unicode.hh" { cs = deserialize_text_unicode_start; } -#line 159 "hb-buffer-deserialize-text-unicode.hh" +#line 150 "hb-buffer-deserialize-text-unicode.hh" { int _slen; int _trans; @@ -215,7 +215,7 @@ _resume: hb_memset (&info, 0, sizeof (info)); } break; -#line 219 "hb-buffer-deserialize-text-unicode.hh" +#line 203 "hb-buffer-deserialize-text-unicode.hh" } _again: @@ -238,7 +238,7 @@ _again: *end_ptr = p; } break; -#line 242 "hb-buffer-deserialize-text-unicode.hh" +#line 224 "hb-buffer-deserialize-text-unicode.hh" } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc index 763172818c4..6787da6f65f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc @@ -427,7 +427,7 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. Then, * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, `=` then #hb_glyph_info_t.cluster. * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format: - * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then, + * - If #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not both 0, `@x_offset,y_offset`. Then, * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then, * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the #hb_glyph_extents_t in the format `` * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc index 56299d9b7e0..0c9190bf6f8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-verify.cc @@ -163,7 +163,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer, hb_buffer_append (fragment, text_buffer, text_start, text_end); if (!hb_shape_full (font, fragment, features, num_features, shapers) || - fragment->successful || fragment->shaping_failed) + fragment->successful) { hb_buffer_destroy (reconstruction); hb_buffer_destroy (fragment); @@ -313,11 +313,11 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, * Shape the two fragment streams. */ if (!hb_shape_full (font, fragments[0], features, num_features, shapers) || - !fragments[0]->successful || fragments[0]->shaping_failed) + !fragments[0]->successful) goto out; if (!hb_shape_full (font, fragments[1], features, num_features, shapers) || - !fragments[1]->successful || fragments[1]->shaping_failed) + !fragments[1]->successful) goto out; if (!forward) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc index c0600fd839b..8f6da312cdd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc @@ -158,14 +158,15 @@ hb_segment_properties_overlay (hb_segment_properties_t *p, bool hb_buffer_t::enlarge (unsigned int size) { - if (unlikely (!successful)) - return false; if (unlikely (size > max_len)) { successful = false; return false; } + if (unlikely (!successful)) + return false; + unsigned int new_allocated = allocated; hb_glyph_position_t *new_pos = nullptr; hb_glyph_info_t *new_info = nullptr; @@ -226,6 +227,13 @@ hb_buffer_t::shift_forward (unsigned int count) assert (have_output); if (unlikely (!ensure (len + count))) return false; + max_ops -= len - idx; + if (unlikely (max_ops < 0)) + { + successful = false; + return false; + } + memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); if (idx + count > len) { @@ -297,7 +305,6 @@ hb_buffer_t::clear () props = default_props; successful = true; - shaping_failed = false; have_output = false; have_positions = false; @@ -320,7 +327,6 @@ hb_buffer_t::enter () { deallocate_var_all (); serial = 0; - shaping_failed = false; scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; unsigned mul; if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR, &mul))) @@ -339,7 +345,6 @@ hb_buffer_t::leave () max_ops = HB_BUFFER_MAX_OPS_DEFAULT; deallocate_var_all (); serial = 0; - // Intentionally not reseting shaping_failed, such that it can be inspected. } @@ -520,7 +525,19 @@ hb_buffer_t::set_masks (hb_mask_t value, hb_mask_t not_mask = ~mask; value &= mask; + max_ops -= len; + if (unlikely (max_ops < 0)) + successful = false; + unsigned int count = len; + + if (cluster_start == 0 && cluster_end == (unsigned int) -1) + { + for (unsigned int i = 0; i < count; i++) + info[i].mask = (info[i].mask & not_mask) | value; + return; + } + for (unsigned int i = 0; i < count; i++) if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) info[i].mask = (info[i].mask & not_mask) | value; @@ -536,6 +553,10 @@ hb_buffer_t::merge_clusters_impl (unsigned int start, return; } + max_ops -= end - start; + if (unlikely (max_ops < 0)) + successful = false; + unsigned int cluster = info[start].cluster; for (unsigned int i = start + 1; i < end; i++) @@ -569,6 +590,10 @@ hb_buffer_t::merge_out_clusters (unsigned int start, if (unlikely (end - start < 2)) return; + max_ops -= end - start; + if (unlikely (max_ops < 0)) + successful = false; + unsigned int cluster = out_info[start].cluster; for (unsigned int i = start + 1; i < end; i++) @@ -726,7 +751,6 @@ DEFINE_NULL_INSTANCE (hb_buffer_t) = HB_SEGMENT_PROPERTIES_DEFAULT, false, /* successful */ - true, /* shaping_failed */ false, /* have_output */ true /* have_positions */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh index 81c0f4a72e3..1c46981cc9c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh @@ -32,6 +32,7 @@ #include "hb.hh" #include "hb-unicode.hh" +#include "hb-set-digest.hh" static_assert ((sizeof (hb_glyph_info_t) == 20), ""); @@ -44,14 +45,14 @@ HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t); enum hb_buffer_scratch_flags_t { HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, - HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, + HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH = 0x00000001u, HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u, - HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u, - HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000040u, - HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK= 0x00000080u, + HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000020u, + HB_BUFFER_SCRATCH_FLAG_HAS_VARIATION_SELECTOR_FALLBACK= 0x00000040u, + HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS = 0x00000080u, /* Reserved for shapers' internal use. */ HB_BUFFER_SCRATCH_FLAG_SHAPER0 = 0x01000000u, @@ -90,7 +91,6 @@ struct hb_buffer_t hb_segment_properties_t props; /* Script, language, direction */ bool successful; /* Allocations successful */ - bool shaping_failed; /* Shaping failure */ bool have_output; /* Whether we have an output buffer going on */ bool have_positions; /* Whether we have positions */ @@ -110,6 +110,7 @@ struct hb_buffer_t hb_codepoint_t context[2][CONTEXT_LENGTH]; unsigned int context_len[2]; + hb_set_digest_t digest; /* Manually updated sometimes */ /* * Managed by enter / leave @@ -200,6 +201,12 @@ struct hb_buffer_t void collect_codepoints (set_t &d) const { d.clear (); d.add_array (&info[0].codepoint, len, sizeof (info[0])); } + void update_digest () + { + digest = hb_set_digest_t (); + collect_codepoints (digest); + } + HB_INTERNAL void similar (const hb_buffer_t &src); HB_INTERNAL void reset (); HB_INTERNAL void clear (); @@ -346,7 +353,7 @@ struct hb_buffer_t { if (out_info != info || out_len != idx) { - if (unlikely (!make_room_for (1, 1))) return false; + if (unlikely (!ensure (out_len + 1))) return false; out_info[out_len] = info[idx]; } out_len++; @@ -363,7 +370,7 @@ struct hb_buffer_t { if (out_info != info || out_len != idx) { - if (unlikely (!make_room_for (n, n))) return false; + if (unlikely (!ensure (out_len + n))) return false; memmove (out_info + out_len, info + idx, n * sizeof (out_info[0])); } out_len += n; @@ -404,22 +411,12 @@ struct hb_buffer_t /* Adds glyph flags in mask to infos with clusters between start and end. * The start index will be from out-buffer if from_out_buffer is true. * If interior is true, then the cluster having the minimum value is skipped. */ - void _set_glyph_flags (hb_mask_t mask, - unsigned start = 0, - unsigned end = (unsigned) -1, - bool interior = false, - bool from_out_buffer = false) + void _set_glyph_flags_impl (hb_mask_t mask, + unsigned start, + unsigned end, + bool interior, + bool from_out_buffer) { - end = hb_min (end, len); - - if (unlikely (end - start > 255)) - return; - - if (interior && !from_out_buffer && end - start < 2) - return; - - scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; - if (!from_out_buffer || !have_output) { if (!interior) @@ -456,6 +453,25 @@ struct hb_buffer_t } } + HB_ALWAYS_INLINE + void _set_glyph_flags (hb_mask_t mask, + unsigned start = 0, + unsigned end = (unsigned) -1, + bool interior = false, + bool from_out_buffer = false) + { + if (unlikely (end != (unsigned) -1 && end - start > 255)) + return; + + end = hb_min (end, len); + + if (interior && !from_out_buffer && end - start < 2) + return; + + _set_glyph_flags_impl (mask, start, end, interior, from_out_buffer); + } + + void unsafe_to_break (unsigned int start = 0, unsigned int end = -1) { _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, @@ -606,6 +622,10 @@ struct hb_buffer_t if (unlikely (start == end)) return; + max_ops -= end - start; + if (unlikely (max_ops < 0)) + successful = false; + unsigned cluster_first = infos[start].cluster; unsigned cluster_last = infos[end - 1].cluster; @@ -614,10 +634,7 @@ struct hb_buffer_t { for (unsigned int i = start; i < end; i++) if (cluster != infos[i].cluster) - { - scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; infos[i].mask |= mask; - } return; } @@ -626,18 +643,12 @@ struct hb_buffer_t if (cluster == cluster_first) { for (unsigned int i = end; start < i && infos[i - 1].cluster != cluster_first; i--) - { - scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; infos[i - 1].mask |= mask; - } } else /* cluster == cluster_last */ { for (unsigned int i = start; i < end && infos[i].cluster != cluster_last; i++) - { - scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; infos[i].mask |= mask; - } } } unsigned diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh index 7d09ca89d7f..3609ccecc71 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cache.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cache.hh @@ -64,17 +64,23 @@ template , - hb_atomic_t>::type, - typename std::conditional::type + typename std::conditional, + typename std::conditional, + hb_atomic_t>::type>::type, + typename std::conditional::type>::type >::type; static_assert ((key_bits >= cache_bits), ""); static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), ""); + static constexpr unsigned MAX_VALUE = (1u << value_bits) - 1; + hb_cache_t () { clear (); } void clear () @@ -83,25 +89,32 @@ struct hb_cache_t v = -1; } + HB_HOT bool get (unsigned int key, unsigned int *value) const { unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits)) return false; *value = v & ((1u<> key_bits) || (value >> value_bits))) - return false; /* Overflows */ + return; /* Overflows */ + set_unchecked (key, value); + } + + HB_HOT + void set_unchecked (unsigned int key, unsigned int value) + { unsigned int k = key & ((1u<>cache_bits)< cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, const int *coords_=nullptr, unsigned int num_coords_=0) : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs), - cached_scalars_vector (&acc.cached_scalars_vector) + region_count (0), cached_scalars_vector (&acc.cached_scalars_vector) { coords = coords_; num_coords = num_coords_; varStore = acc.varStore; - do_blend = num_coords && coords && varStore->size; + do_blend = num_coords && varStore->size; set_ivs (acc.privateDicts[fd].ivs); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-common.cc index 5c8546a378c..9eeae358a75 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.cc @@ -40,43 +40,6 @@ **/ -/* hb_options_t */ - -hb_atomic_t _hb_options; - -void -_hb_options_init () -{ - hb_options_union_t u; - u.i = 0; - u.opts.initialized = true; - - const char *c = getenv ("HB_OPTIONS"); - if (c) - { - while (*c) - { - const char *p = strchr (c, ':'); - if (!p) - p = c + strlen (c); - -#define OPTION(name, symbol) \ - if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast(p - c)) do { u.opts.symbol = true; } while (0) - - OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); - -#undef OPTION - - c = *p ? p + 1 : p; - } - - } - - /* This is idempotent and threadsafe. */ - _hb_options = u.i; -} - - /* hb_tag_t */ /** @@ -545,8 +508,11 @@ hb_script_to_iso15924_tag (hb_script_t script) * Fetches the #hb_direction_t of a script when it is * set horizontally. All right-to-left scripts will return * #HB_DIRECTION_RTL. All left-to-right scripts will return - * #HB_DIRECTION_LTR. Scripts that can be written either - * horizontally or vertically will return #HB_DIRECTION_INVALID. + * #HB_DIRECTION_LTR. + * + * Scripts that can be written either right-to-left or + * left-to-right will return #HB_DIRECTION_INVALID. + * * Unknown scripts will return #HB_DIRECTION_LTR. * * Return value: The horizontal #hb_direction_t of @script @@ -628,6 +594,9 @@ hb_script_get_horizontal_direction (hb_script_t script) /* Unicode-16.0 additions */ case HB_SCRIPT_GARAY: + /* Unicode-17.0 additions */ + case HB_SCRIPT_SIDETIC: + return HB_DIRECTION_RTL; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-config.hh b/src/java.desktop/share/native/libharfbuzz/hb-config.hh index 3956690da35..c522eeea2ea 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-config.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-config.hh @@ -38,7 +38,6 @@ #ifndef HB_EXPERIMENTAL_API #define HB_NO_BEYOND_64K #define HB_NO_CUBIC_GLYF -#define HB_NO_VAR_COMPOSITES #endif #ifdef HB_TINY @@ -91,7 +90,10 @@ #ifdef HB_MINI #define HB_NO_AAT #define HB_NO_LEGACY -#define HB_NO_BORING_EXPANSION +#define HB_NO_BEYOND_64K +#define HB_NO_CUBIC_GLYF +#define HB_NO_VAR_COMPOSITES +#define HB_NO_VAR_HVF #endif #ifdef __OPTIMIZE_SIZE__ @@ -109,12 +111,6 @@ /* Closure of options. */ -#ifdef HB_NO_BORING_EXPANSION -#define HB_NO_BEYOND_64K -#define HB_NO_CUBIC_GLYF -#define HB_NO_VAR_COMPOSITES -#endif - #ifdef HB_NO_VAR #define HB_NO_VAR_COMPOSITES #endif @@ -149,10 +145,6 @@ #define HB_NO_PAINT #endif -#ifdef HB_NO_GETENV -#define HB_NO_UNISCRIBE_BUG_COMPATIBLE -#endif - #ifdef HB_NO_LEGACY #define HB_NO_CMAP_LEGACY_SUBTABLES #define HB_NO_FALLBACK_SHAPE diff --git a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh index 1e9a18b9326..87d30d9ded1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh @@ -37,48 +37,6 @@ #endif -/* - * Global runtime options. - */ - -struct hb_options_t -{ - bool unused : 1; /* In-case sign bit is here. */ - bool initialized : 1; - bool uniscribe_bug_compatible : 1; -}; - -union hb_options_union_t { - unsigned i; - hb_options_t opts; -}; -static_assert ((sizeof (hb_atomic_t) >= sizeof (hb_options_union_t)), ""); - -HB_INTERNAL void -_hb_options_init (); - -extern HB_INTERNAL hb_atomic_t _hb_options; - -static inline hb_options_t -hb_options () -{ -#ifdef HB_NO_GETENV - return hb_options_t (); -#endif - /* Make a local copy, so we can access bitfield threadsafely. */ - hb_options_union_t u; - u.i = _hb_options; - - if (unlikely (!u.i)) - { - _hb_options_init (); - u.i = _hb_options; - } - - return u.opts; -} - - /* * Debug output (needs enabling at compile time.) */ @@ -394,6 +352,10 @@ struct hb_no_trace_t { #define HB_DEBUG_WASM (HB_DEBUG+0) #endif +#ifndef HB_DEBUG_KBTS +#define HB_DEBUG_KBTS (HB_DEBUG+0) +#endif + /* * With tracing. */ @@ -484,7 +446,7 @@ struct hb_no_trace_t { #ifndef HB_BUFFER_MESSAGE_MORE -#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+1) +#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+0) #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h index 927563c4c40..a87a53d0e30 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h @@ -287,7 +287,7 @@ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * * Since: 7.0.0 - * XDeprecated: REPLACEME: Use hb_font_draw_glyph_func_or_fail_t instead. + * Deprecated: 11.2.0: Use hb_font_draw_glyph_func_or_fail_t instead. **/ typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -308,7 +308,7 @@ typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data, * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * * Since: 7.0.0 - * XDeprecated: REPLACEME: Use hb_font_paint_glyph_or_fail_func_t instead. + * Deprecated: 11.2.0: Use hb_font_paint_glyph_or_fail_func_t instead. */ typedef hb_bool_t (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -346,7 +346,7 @@ hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, * Sets the implementation function for #hb_font_draw_glyph_func_t. * * Since: 7.0.0 - * XDeprecated: REPLACEME: Use hb_font_funcs_set_draw_glyph_or_fail_func instead. + * Deprecated: 11.2.0: Use hb_font_funcs_set_draw_glyph_or_fail_func instead. **/ HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_or_fail_func) HB_EXTERN void @@ -364,7 +364,7 @@ hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs, * Sets the implementation function for #hb_font_paint_glyph_func_t. * * Since: 7.0.0 - * XDeprecated: REPLACEME: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead. + * Deprecated: 11.2.0: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead. */ HB_DEPRECATED_FOR (hb_font_funcs_set_paint_glyph_or_fail_func) HB_EXTERN void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc index 8764ffe7129..c243d12f7ae 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc @@ -63,14 +63,14 @@ hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data, float to_x, float to_y, void *user_data HB_UNUSED) { -#define HB_ONE_THIRD 0.33333333f +#define HB_TWO_THIRD 0.66666666666666666666666667f dfuncs->emit_cubic_to (draw_data, *st, - (st->current_x + 2.f * control_x) * HB_ONE_THIRD, - (st->current_y + 2.f * control_y) * HB_ONE_THIRD, - (to_x + 2.f * control_x) * HB_ONE_THIRD, - (to_y + 2.f * control_y) * HB_ONE_THIRD, + st->current_x + (control_x - st->current_x) * HB_TWO_THIRD, + st->current_y + (control_y - st->current_y) * HB_TWO_THIRD, + to_x + (control_x - to_x) * HB_TWO_THIRD, + to_y + (control_y - to_y) * HB_TWO_THIRD, to_x, to_y); -#undef HB_ONE_THIRD +#undef HB_TWO_THIRD } static void @@ -467,7 +467,7 @@ hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_extents_t *extents = (hb_extents_t *) data; + hb_extents_t<> *extents = (hb_extents_t<> *) data; extents->add_point (to_x, to_y); } @@ -479,7 +479,7 @@ hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_extents_t *extents = (hb_extents_t *) data; + hb_extents_t<> *extents = (hb_extents_t<> *) data; extents->add_point (to_x, to_y); } @@ -492,7 +492,7 @@ hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_extents_t *extents = (hb_extents_t *) data; + hb_extents_t<> *extents = (hb_extents_t<> *) data; extents->add_point (control_x, control_y); extents->add_point (to_x, to_y); @@ -507,7 +507,7 @@ hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, float to_x, float to_y, void *user_data HB_UNUSED) { - hb_extents_t *extents = (hb_extents_t *) data; + hb_extents_t<> *extents = (hb_extents_t<> *) data; extents->add_point (control1_x, control1_y); extents->add_point (control2_x, control2_y); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc index 6b7e1913024..c1feaac6280 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face-builder.cc @@ -169,8 +169,7 @@ _hb_face_builder_get_table_tags (const hb_face_t *face HB_UNUSED, if (unlikely (start_offset >= population)) { - if (table_count) - *table_count = 0; + *table_count = 0; return population; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-face.cc index 9d59d7c9468..a0f497eea7f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.cc @@ -84,8 +84,7 @@ hb_face_count (hb_blob_t *blob) hb_sanitize_context_t c (blob); - const char *start = hb_blob_get_data (blob, nullptr); - auto *ot = reinterpret_cast (const_cast (start)); + auto *ot = blob->as (); if (unlikely (!ot->sanitize (&c))) return 0; @@ -329,7 +328,7 @@ hb_face_create_from_file_or_fail (const char *file_name, return face; } -static struct supported_face_loaders_t { +static const struct supported_face_loaders_t { char name[16]; hb_face_t * (*from_file) (const char *font_file, unsigned face_index); hb_face_t * (*from_blob) (hb_blob_t *blob, unsigned face_index); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-font.cc index 849855e2c22..6c636e23567 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.cc @@ -246,7 +246,6 @@ hb_font_get_glyph_v_advance_nil (hb_font_t *font, hb_codepoint_t glyph HB_UNUSED, void *user_data HB_UNUSED) { - /* TODO use font_extents.ascender+descender */ return -font->y_scale; } @@ -352,6 +351,10 @@ hb_font_get_glyph_h_origin_default (hb_font_t *font, hb_position_t *y, void *user_data HB_UNUSED) { + if (font->has_glyph_h_origins_func_set ()) + { + return font->get_glyph_h_origins (1, &glyph, 0, x, 0, y, 0, false); + } hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); if (ret) font->parent_scale_position (x, y); @@ -366,7 +369,6 @@ hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, hb_position_t *y, void *user_data HB_UNUSED) { - *x = *y = 0; return false; } @@ -378,12 +380,100 @@ hb_font_get_glyph_v_origin_default (hb_font_t *font, hb_position_t *y, void *user_data HB_UNUSED) { + if (font->has_glyph_v_origins_func_set ()) + { + return font->get_glyph_v_origins (1, &glyph, 0, x, 0, y, 0, false); + } hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); if (ret) font->parent_scale_position (x, y); return ret; } +#define hb_font_get_glyph_h_origins_nil hb_font_get_glyph_h_origins_default + +static hb_bool_t +hb_font_get_glyph_h_origins_default (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph HB_UNUSED, + unsigned glyph_stride HB_UNUSED, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_h_origin_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + font->get_glyph_h_origin (*first_glyph, first_x, first_y, false); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + return true; + } + + hb_bool_t ret = font->parent->get_glyph_h_origins (count, + first_glyph, glyph_stride, + first_x, x_stride, + first_y, y_stride); + if (ret) + { + for (unsigned i = 0; i < count; i++) + { + font->parent_scale_position (first_x, first_y); + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + return ret; +} + +#define hb_font_get_glyph_v_origins_nil hb_font_get_glyph_v_origins_default + +static hb_bool_t +hb_font_get_glyph_v_origins_default (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph HB_UNUSED, + unsigned glyph_stride HB_UNUSED, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_v_origin_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + font->get_glyph_v_origin (*first_glyph, first_x, first_y, false); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + return true; + } + + hb_bool_t ret = font->parent->get_glyph_v_origins (count, + first_glyph, glyph_stride, + first_x, x_stride, + first_y, y_stride); + if (ret) + { + for (unsigned i = 0; i < count; i++) + { + font->parent_scale_position (first_x, first_y); + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + return ret; +} + static hb_position_t hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED, @@ -1256,6 +1346,77 @@ hb_font_get_glyph_v_origin (hb_font_t *font, return font->get_glyph_v_origin (glyph, x, y); } +/** + * hb_font_get_glyph_h_origins: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_x: (out): The first X coordinate of the origin retrieved + * @x_stride: The stride between successive X coordinates + * @first_y: (out): The first Y coordinate of the origin retrieved + * @y_stride: The stride between successive Y coordinates + * + * Fetches the (X,Y) coordinates of the origin for requested glyph IDs + * in the specified font, for horizontal text segments. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 11.3.0 + **/ +hb_bool_t +hb_font_get_glyph_h_origins (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_x, + unsigned int x_stride, + hb_position_t *first_y, + unsigned int y_stride) + +{ + return font->get_glyph_h_origins (count, + first_glyph, glyph_stride, + first_x, x_stride, + first_y, y_stride); +} + +/** + * hb_font_get_glyph_v_origins: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_x: (out): The first X coordinate of the origin retrieved + * @x_stride: The stride between successive X coordinates + * @first_y: (out): The first Y coordinate of the origin retrieved + * @y_stride: The stride between successive Y coordinates + * + * Fetches the (X,Y) coordinates of the origin for requested glyph IDs + * in the specified font, for vertical text segments. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 11.3.0 + **/ +hb_bool_t +hb_font_get_glyph_v_origins (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_x, + unsigned int x_stride, + hb_position_t *first_y, + unsigned int y_stride) + +{ + return font->get_glyph_v_origins (count, + first_glyph, glyph_stride, + first_x, x_stride, + first_y, y_stride); +} + + /** * hb_font_get_glyph_h_kerning: * @font: #hb_font_t to work upon @@ -1443,7 +1604,7 @@ hb_font_get_glyph_shape (hb_font_t *font, * * Return value: `true` if glyph was drawn, `false` otherwise * - * XSince: REPLACEME + * Since: 11.2.0 **/ hb_bool_t hb_font_draw_glyph_or_fail (hb_font_t *font, @@ -1480,7 +1641,7 @@ hb_font_draw_glyph_or_fail (hb_font_t *font, * * Return value: `true` if glyph was painted, `false` otherwise * - * XSince: REPLACEME + * Since: 11.2.0 */ hb_bool_t hb_font_paint_glyph_or_fail (hb_font_t *font, @@ -1883,6 +2044,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 1000, /* x_scale */ 1000, /* y_scale */ + false, /* is_synthetic */ 0.f, /* x_embolden */ 0.f, /* y_embolden */ true, /* embolden_in_place */ @@ -1900,6 +2062,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 0, /* ptem */ HB_FONT_NO_VAR_NAMED_INSTANCE, /* instance_index */ + false, /* has_nonzero_coords */ 0, /* num_coords */ nullptr, /* coords */ nullptr, /* design_coords */ @@ -1960,8 +2123,14 @@ hb_font_create (hb_face_t *face) hb_font_set_funcs_using (font, nullptr); #ifndef HB_NO_VAR - if (face && face->index >> 16) - hb_font_set_var_named_instance (font, (face->index >> 16) - 1); + // Initialize variations. + if (likely (face)) + { + if (face->index >> 16) + hb_font_set_var_named_instance (font, (face->index >> 16) - 1); + else + hb_font_set_variations (font, nullptr, 0); + } #endif return font; @@ -1979,6 +2148,7 @@ _hb_font_adopt_var_coords (hb_font_t *font, font->coords = coords; font->design_coords = design_coords; font->num_coords = coords_length; + font->has_nonzero_coords = hb_any (hb_array (coords, coords_length)); font->changed (); font->serial_coords = font->serial; @@ -2393,7 +2563,7 @@ hb_font_set_funcs_data (hb_font_t *font, font->changed (); } -static struct supported_font_funcs_t { +static const struct supported_font_funcs_t { char name[16]; void (*func) (hb_font_t *); } supported_font_funcs[] = @@ -2450,6 +2620,9 @@ hb_bool_t hb_font_set_funcs_using (hb_font_t *font, const char *name) { + if (unlikely (hb_object_is_immutable (font))) + return false; + bool retry = false; if (!name || !*name) @@ -2704,12 +2877,12 @@ hb_font_get_ptem (hb_font_t *font) * * Return value: `true` if the font is synthetic, `false` otherwise. * - * XSince: REPLACEME + * Since: 11.2.0 */ hb_bool_t hb_font_is_synthetic (hb_font_t *font) { - return font->is_synthetic (); + return font->is_synthetic; } /** @@ -2858,12 +3031,6 @@ hb_font_set_variations (hb_font_t *font, if (hb_object_is_immutable (font)) return; - if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE) - { - hb_font_set_var_coords_normalized (font, nullptr, 0); - return; - } - const OT::fvar &fvar = *font->face->table.fvar; auto axes = fvar.get_axes (); const unsigned coords_length = axes.length; @@ -2970,7 +3137,6 @@ hb_font_set_variation (hb_font_t *font, hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized); _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); - } /** @@ -2991,11 +3157,16 @@ hb_font_set_variation (hb_font_t *font, void hb_font_set_var_coords_design (hb_font_t *font, const float *coords, - unsigned int coords_length) + unsigned int input_coords_length) { if (hb_object_is_immutable (font)) return; + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + const unsigned coords_length = axes.length; + + input_coords_length = hb_min (input_coords_length, coords_length); int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; @@ -3006,8 +3177,11 @@ hb_font_set_var_coords_design (hb_font_t *font, return; } - if (coords_length) - hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); + if (input_coords_length) + hb_memcpy (design_coords, coords, input_coords_length * sizeof (font->design_coords[0])); + // Fill in the rest with default values + for (unsigned int i = input_coords_length; i < coords_length; i++) + design_coords[i] = axes[i].get_default (); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); @@ -3072,34 +3246,31 @@ hb_font_get_var_named_instance (hb_font_t *font) void hb_font_set_var_coords_normalized (hb_font_t *font, const int *coords, /* 2.14 normalized */ - unsigned int coords_length) + unsigned int input_coords_length) { if (hb_object_is_immutable (font)) return; + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + unsigned coords_length = axes.length; + + input_coords_length = hb_min (input_coords_length, coords_length); int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; - int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr; - if (unlikely (coords_length && !(copy && unmapped && design_coords))) + if (unlikely (coords_length && !(copy && design_coords))) { hb_free (copy); - hb_free (unmapped); hb_free (design_coords); return; } - if (coords_length) - { - hb_memcpy (copy, coords, coords_length * sizeof (coords[0])); - hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0])); - } + if (input_coords_length) + hb_memcpy (copy, coords, input_coords_length * sizeof (coords[0])); - /* Best effort design coords simulation */ - font->face->table.avar->unmap_coords (unmapped, coords_length); for (unsigned int i = 0; i < coords_length; ++i) - design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); - hb_free (unmapped); + design_coords[i] = NAN; _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); } @@ -3112,8 +3283,8 @@ hb_font_set_var_coords_normalized (hb_font_t *font, * Fetches the list of normalized variation coordinates currently * set on a font. * - * Note that this returned array may only contain values for some - * (or none) of the axes; omitted axes effectively have zero values. + * Note that if no variation coordinates are set, this function may + * return %NULL. * * Return value is valid as long as variation coordinates of the font * are not modified. @@ -3140,9 +3311,12 @@ hb_font_get_var_coords_normalized (hb_font_t *font, * Fetches the list of variation coordinates (in design-space units) currently * set on a font. * - * Note that this returned array may only contain values for some - * (or none) of the axes; omitted axes effectively have their default - * values. + * Note that if no variation coordinates are set, this function may + * return %NULL. + * + * If variations have been set on the font using normalized coordinates + * (i.e. via hb_font_set_var_coords_normalized()), the design coordinates will + * have NaN (Not a Number) values. * * Return value is valid as long as variation coordinates of the font * are not modified. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.h b/src/java.desktop/share/native/libharfbuzz/hb-font.h index 4e267ba6a80..2d06b659278 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.h @@ -97,7 +97,7 @@ hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); * @descender: The depth of typographic descenders. * @line_gap: The suggested line-spacing gap. * - * Font-wide extent values, measured in font units. + * Font-wide extent values, measured in scaled units. * * Note that typically @ascender is positive and @descender * negative, in coordinate systems that grow up. @@ -332,7 +332,7 @@ typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t; * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * This method should retrieve the (X,Y) coordinates (in font units) of the + * This method should retrieve the (X,Y) coordinates (in scaled units) of the * origin for a glyph. Each coordinate must be returned in an #hb_position_t * output parameter. * @@ -349,7 +349,7 @@ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *fon * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * This method should retrieve the (X,Y) coordinates (in font units) of the + * This method should retrieve the (X,Y) coordinates (in scaled units) of the * origin for a glyph, for horizontal-direction text segments. Each * coordinate must be returned in an #hb_position_t output parameter. * @@ -361,13 +361,72 @@ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * This method should retrieve the (X,Y) coordinates (in font units) of the + * This method should retrieve the (X,Y) coordinates (in scaled units) of the * origin for a glyph, for vertical-direction text segments. Each coordinate * must be returned in an #hb_position_t output parameter. * **/ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; +/** + * hb_font_get_glyph_origins_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @first_glyph: The first glyph ID to query + * @count: number of glyphs to query + * @glyph_stride: The stride between successive glyph IDs + * @first_x: (out): The first origin X coordinate retrieved + * @x_stride: The stride between successive origin X coordinates + * @first_y: (out): The first origin Y coordinate retrieved + * @y_stride: The stride between successive origin Y coordinates + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in scaled units) of the + * origin for each requested glyph. Each coordinate value must be returned in + * an #hb_position_t in the two output parameters. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 11.3.0 + **/ +typedef hb_bool_t (*hb_font_get_glyph_origins_func_t) (hb_font_t *font, void *font_data, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride, + void *user_data); + +/** + * hb_font_get_glyph_h_origins_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in scaled units) of the + * origin for requested glyph, for horizontal-direction text segments. Each + * coordinate must be returned in a the x/y #hb_position_t output parameters. + * + * Since: 11.3.0 + **/ +typedef hb_font_get_glyph_origins_func_t hb_font_get_glyph_h_origins_func_t; + +/** + * hb_font_get_glyph_v_origins_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in scaled units) of the + * origin for requested glyph, for vertical-direction text segments. Each + * coordinate must be returned in a the x/y #hb_position_t output parameters. + * + * Since: 11.3.0 + **/ +typedef hb_font_get_glyph_origins_func_t hb_font_get_glyph_v_origins_func_t; + /** * hb_font_get_glyph_kerning_func_t: * @font: #hb_font_t to work upon @@ -428,7 +487,7 @@ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *fo * * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. * - * This method should retrieve the (X,Y) coordinates (in font units) for a + * This method should retrieve the (X,Y) coordinates (in scaled units) for a * specified contour point in a glyph. Each coordinate must be returned as * an #hb_position_t output parameter. * @@ -498,7 +557,7 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void * * * Return value: `true` if glyph was drawn, `false` otherwise * - * XSince: REPLACEME + * Since: 11.2.0 **/ typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -520,7 +579,7 @@ typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *f * * Return value: `true` if glyph was painted, `false` otherwise * - * XSince: REPLACEME + * Since: 11.2.0 */ typedef hb_bool_t (*hb_font_paint_glyph_or_fail_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -707,6 +766,38 @@ hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_v_origin_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_glyph_h_origins_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_h_origins_func_t. + * + * Since: 11.3.0 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_origins_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_origins_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_origins_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_origins_func_t. + * + * Since: 11.3.0 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_origins_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_origins_func_t func, + void *user_data, hb_destroy_func_t destroy); + /** * hb_font_funcs_set_glyph_h_kerning_func: * @ffuncs: A font-function structure @@ -796,7 +887,7 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, * * Sets the implementation function for #hb_font_draw_glyph_or_fail_func_t. * - * XSince: REPLACEME + * Since: 11.2.0 **/ HB_EXTERN void hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs, @@ -812,7 +903,7 @@ hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs, * * Sets the implementation function for #hb_font_paint_glyph_or_fail_func_t. * - * XSince: REPLACEME + * Since: 11.2.0 */ HB_EXTERN void hb_font_funcs_set_paint_glyph_or_fail_func (hb_font_funcs_t *ffuncs, @@ -876,6 +967,26 @@ hb_font_get_glyph_v_origin (hb_font_t *font, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y); +HB_EXTERN hb_bool_t +hb_font_get_glyph_h_origins (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_v_origins (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride); + HB_EXTERN hb_position_t hb_font_get_glyph_h_kerning (hb_font_t *font, hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.hh b/src/java.desktop/share/native/libharfbuzz/hb-font.hh index 69d8d4b09df..9dd54466d7f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.hh @@ -55,6 +55,8 @@ HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advances) \ HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origin) \ HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origins) \ + HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origins) \ HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_kerning) \ HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_kerning)) \ HB_FONT_FUNC_IMPLEMENT (get_,glyph_extents) \ @@ -118,6 +120,8 @@ struct hb_font_t int32_t x_scale; int32_t y_scale; + bool is_synthetic; + float x_embolden; float y_embolden; bool embolden_in_place; @@ -139,6 +143,7 @@ struct hb_font_t /* Font variation coordinates. */ unsigned int instance_index; + bool has_nonzero_coords; unsigned int num_coords; int *coords; float *design_coords; @@ -430,21 +435,127 @@ struct hb_font_t } hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y, + bool synthetic = true) { *x = *y = 0; - return klass->get.f.glyph_h_origin (this, user_data, - glyph, x, y, - !klass->user_data ? nullptr : klass->user_data->glyph_h_origin); + bool ret = klass->get.f.glyph_h_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_h_origin); + + if (synthetic && ret) + { + /* Slant is ignored as it does not affect glyph origin */ + + /* Embolden */ + if (!embolden_in_place) + { + *x += x_scale < 0 ? -x_strength : x_strength; + *y += y_scale < 0 ? -y_strength : y_strength; + } + } + + return ret; } hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y, + bool synthetic = true) { *x = *y = 0; - return klass->get.f.glyph_v_origin (this, user_data, - glyph, x, y, - !klass->user_data ? nullptr : klass->user_data->glyph_v_origin); + bool ret = klass->get.f.glyph_v_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_v_origin); + + if (synthetic && ret) + { + /* Slant is ignored as it does not affect glyph origin */ + + /* Embolden */ + if (!embolden_in_place) + { + *x += x_scale < 0 ? -x_strength : x_strength; + *y += y_scale < 0 ? -y_strength : y_strength; + } + } + + return ret; + } + + hb_bool_t get_glyph_h_origins (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_x, + unsigned int x_stride, + hb_position_t *first_y, + unsigned int y_stride, + bool synthetic = true) + + { + bool ret = klass->get.f.glyph_h_origins (this, user_data, + count, + first_glyph, glyph_stride, + first_x, x_stride, first_y, y_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_h_origins); + + if (synthetic && ret) + { + hb_position_t x_shift = x_scale < 0 ? -x_strength : x_strength; + hb_position_t y_shift = y_scale < 0 ? -y_strength : y_strength; + for (unsigned i = 0; i < count; i++) + { + /* Slant is ignored as it does not affect glyph origin */ + + /* Embolden */ + if (!embolden_in_place) + { + *first_x += x_shift; + *first_y += y_shift; + } + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + + return ret; + } + + hb_bool_t get_glyph_v_origins (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_x, + unsigned int x_stride, + hb_position_t *first_y, + unsigned int y_stride, + bool synthetic = true) + + { + bool ret = klass->get.f.glyph_v_origins (this, user_data, + count, + first_glyph, glyph_stride, + first_x, x_stride, first_y, y_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_v_origins); + + if (synthetic && is_synthetic && ret) + { + hb_position_t x_shift = x_scale < 0 ? -x_strength : x_strength; + hb_position_t y_shift = y_scale < 0 ? -y_strength : y_strength; + for (unsigned i = 0; i < count; i++) + { + /* Slant is ignored as it does not affect glyph origin */ + + /* Embolden */ + if (!embolden_in_place) + { + *first_x += x_shift; + *first_y += y_shift; + } + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + + return ret; } hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, @@ -486,7 +597,7 @@ struct hb_font_t extents, !klass->user_data ? nullptr : klass->user_data->glyph_extents); } - if (!is_synthetic () && + if (!is_synthetic && klass->get.f.glyph_extents (this, user_data, glyph, extents, @@ -496,6 +607,7 @@ struct hb_font_t /* Try getting extents from paint(), then draw(), *then* get_extents() * and apply synthetic settings in the last case. */ +#ifndef HB_NO_PAINT hb_paint_extents_context_t paint_extents; if (paint_glyph_or_fail (glyph, hb_paint_extents_get_funcs (), &paint_extents, @@ -504,14 +616,17 @@ struct hb_font_t *extents = paint_extents.get_extents ().to_glyph_extents (); return true; } +#endif - hb_extents_t draw_extents; +#ifndef HB_NO_DRAW + hb_extents_t<> draw_extents; if (draw_glyph_or_fail (glyph, hb_draw_extents_get_funcs (), &draw_extents)) { *extents = draw_extents.to_glyph_extents (); return true; } +#endif bool ret = klass->get.f.glyph_extents (this, user_data, glyph, @@ -575,6 +690,7 @@ struct hb_font_t hb_draw_funcs_t *draw_funcs, void *draw_data, bool synthetic = true) { +#ifndef HB_NO_DRAW #ifndef HB_NO_OUTLINE bool embolden = x_strength || y_strength; bool slanted = slant_xy; @@ -603,7 +719,13 @@ struct hb_font_t // Slant before embolden; produces nicer results. if (slanted) + { + hb_position_t xo = 0, yo = 0; + get_glyph_h_origin (glyph, &xo, &yo, false); + outline.translate (-xo, -yo); outline.slant (slant_xy); + outline.translate (xo, yo); + } if (embolden) { @@ -618,6 +740,8 @@ struct hb_font_t return true; #endif +#endif + return false; } bool paint_glyph_or_fail (hb_codepoint_t glyph, @@ -626,6 +750,7 @@ struct hb_font_t hb_color_t foreground, bool synthetic = true) { +#ifndef HB_NO_PAINT /* Slant */ if (synthetic && slant_xy) hb_paint_push_transform (paint_funcs, paint_data, @@ -643,6 +768,8 @@ struct hb_font_t hb_paint_pop_transform (paint_funcs, paint_data); return ret; +#endif + return false; } /* A bit higher-level, and with fallback */ @@ -704,6 +831,28 @@ struct hb_font_t get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); } + void apply_offset (hb_position_t *x, hb_position_t *y, + hb_position_t dx, hb_position_t dy, + signed mult) + { + assert (mult == -1 || mult == +1); + + *x += dx * mult; + *y += dy * mult; + } + void add_offset (hb_position_t *x, hb_position_t *y, + hb_position_t dx, hb_position_t dy) + { + *x += dx; + *y += dy; + } + void subtract_offset (hb_position_t *x, hb_position_t *y, + hb_position_t dx, hb_position_t dy) + { + *x -= dx; + *y -= dy; + } + void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y) { @@ -714,6 +863,141 @@ struct hb_font_t *y = extents.ascender; } + void apply_glyph_h_origins_with_fallback (hb_buffer_t *buf, int mult) + { + bool has_ascender = false; + hb_position_t ascender = 0; + + struct { hb_position_t x, y; } origins[32]; + + unsigned int offset = 0; + unsigned int count = buf->len; + while (offset < count) + { + unsigned n = hb_min (count - offset, ARRAY_LENGTH (origins)); + if (!get_glyph_h_origins (n, + &buf->info[offset].codepoint, sizeof (hb_glyph_info_t), + &origins[0].x, sizeof (origins[0]), + &origins[0].y, sizeof (origins[0]))) + { + if (get_glyph_v_origins (n, + &buf->info[offset].codepoint, sizeof (hb_glyph_info_t), + &origins[0].x, sizeof (origins[0]), + &origins[0].y, sizeof (origins[0]))) + { + if (!has_ascender) + { + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + ascender = extents.ascender; + has_ascender = true; + } + + /* We got the v_origins, adjust them to h_origins. */ + for (unsigned j = 0; j < n; j++) + { + hb_codepoint_t glyph = buf->info[offset + j].codepoint; + origins[j].x -= get_glyph_h_advance (glyph) / 2; + origins[j].y -= ascender; + } + } + else + { + for (unsigned j = 0; j < n; j++) + { + origins[j].x = 0; + origins[j].y = 0; + } + } + } + + assert (mult == -1 || mult == +1); + if (mult == +1) + for (unsigned j = 0; j < n; j++) + { + hb_glyph_position_t *pos = &buf->pos[offset + j]; + add_offset (&pos->x_offset, &pos->y_offset, + origins[j].x, origins[j].y); + } + else /* mult == -1 */ + for (unsigned j = 0; j < n; j++) + { + hb_glyph_position_t *pos = &buf->pos[offset + j]; + subtract_offset (&pos->x_offset, &pos->y_offset, + origins[j].x, origins[j].y); + } + + offset += n; + } + } + void apply_glyph_v_origins_with_fallback (hb_buffer_t *buf, int mult) + { + bool has_ascender = false; + hb_position_t ascender = 0; + + struct { hb_position_t x, y; } origins[32]; + + unsigned int offset = 0; + unsigned int count = buf->len; + while (offset < count) + { + unsigned n = hb_min (count - offset, ARRAY_LENGTH (origins)); + if (!get_glyph_v_origins (n, + &buf->info[offset].codepoint, sizeof (hb_glyph_info_t), + &origins[0].x, sizeof (origins[0]), + &origins[0].y, sizeof (origins[0]))) + { + if (get_glyph_h_origins (n, + &buf->info[offset].codepoint, sizeof (hb_glyph_info_t), + &origins[0].x, sizeof (origins[0]), + &origins[0].y, sizeof (origins[0]))) + { + if (!has_ascender) + { + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + ascender = extents.ascender; + has_ascender = true; + } + + /* We got the h_origins, adjust them to v_origins. */ + for (unsigned j = 0; j < n; j++) + { + hb_codepoint_t glyph = buf->info[offset + j].codepoint; + origins[j].x += get_glyph_h_advance (glyph) / 2; + origins[j].y += ascender; + } + } + else + { + for (unsigned j = 0; j < n; j++) + { + origins[j].x = 0; + origins[j].y = 0; + } + } + } + + assert (mult == -1 || mult == +1); + if (mult == +1) + for (unsigned j = 0; j < n; j++) + { + hb_glyph_position_t *pos = &buf->pos[offset + j]; + add_offset (&pos->x_offset, &pos->y_offset, + origins[j].x, origins[j].y); + } + else /* mult == -1 */ + for (unsigned j = 0; j < n; j++) + { + hb_glyph_position_t *pos = &buf->pos[offset + j]; + subtract_offset (&pos->x_offset, &pos->y_offset, + origins[j].x, origins[j].y); + } + + offset += n; + } + } + void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y) { @@ -722,7 +1006,7 @@ struct hb_font_t { hb_position_t dx, dy; guess_v_origin_minus_h_origin (glyph, &dx, &dy); - *x -= dx; *y -= dy; + subtract_offset (x, y, dx, dy); } } void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, @@ -733,7 +1017,7 @@ struct hb_font_t { hb_position_t dx, dy; guess_v_origin_minus_h_origin (glyph, &dx, &dy); - *x += dx; *y += dy; + add_offset (x, y, dx, dy); } } @@ -747,68 +1031,38 @@ struct hb_font_t get_glyph_v_origin_with_fallback (glyph, x, y); } - void add_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + void add_glyph_h_origins (hb_buffer_t *buf) { - hb_position_t origin_x, origin_y; - - get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; + apply_glyph_h_origins_with_fallback (buf, +1); } - void add_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + void add_glyph_v_origins (hb_buffer_t *buf) { - hb_position_t origin_x, origin_y; - - get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; + apply_glyph_v_origins_with_fallback (buf, +1); } void add_glyph_origin_for_direction (hb_codepoint_t glyph, hb_direction_t direction, hb_position_t *x, hb_position_t *y) { hb_position_t origin_x, origin_y; - get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); - - *x += origin_x; - *y += origin_y; + add_offset (x, y, origin_x, origin_y); } - void subtract_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + void subtract_glyph_h_origins (hb_buffer_t *buf) { - hb_position_t origin_x, origin_y; - - get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; + apply_glyph_h_origins_with_fallback (buf, -1); } - void subtract_glyph_v_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + void subtract_glyph_v_origins (hb_buffer_t *buf) { - hb_position_t origin_x, origin_y; - - get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; + apply_glyph_v_origins_with_fallback (buf, -1); } void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, hb_direction_t direction, hb_position_t *x, hb_position_t *y) { hb_position_t origin_x, origin_y; - get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); - - *x -= origin_x; - *y -= origin_y; + subtract_offset (x, y, origin_x, origin_y); } void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, @@ -890,11 +1144,6 @@ struct hb_font_t return false; } - bool is_synthetic () const - { - return x_embolden || y_embolden || slant; - } - void changed () { float upem = face->get_upem (); @@ -906,6 +1155,8 @@ struct hb_font_t bool y_neg = y_scale < 0; y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; + is_synthetic = x_embolden || y_embolden || slant; + x_strength = roundf (abs (x_scale) * x_embolden); y_strength = roundf (abs (y_scale) * y_embolden); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-pool.hh b/src/java.desktop/share/native/libharfbuzz/hb-free-pool.hh similarity index 92% rename from src/java.desktop/share/native/libharfbuzz/hb-pool.hh rename to src/java.desktop/share/native/libharfbuzz/hb-free-pool.hh index 613c78d1973..3acd144a857 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-pool.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-free-pool.hh @@ -24,12 +24,12 @@ * Facebook Author(s): Behdad Esfahbod */ -#ifndef HB_POOL_HH -#define HB_POOL_HH +#ifndef HB_FREE_POOL_HH +#define HB_FREE_POOL_HH #include "hb.hh" -/* Memory pool for persistent allocation of small objects. +/* Memory pool for persistent alloc/free of small objects. * * Some AI musings on this, not necessarily true: * @@ -41,10 +41,10 @@ * sophisticated, use a real allocator. Or use a real language. */ template -struct hb_pool_t +struct hb_free_pool_t { - hb_pool_t () : next (nullptr) {} - ~hb_pool_t () + hb_free_pool_t () : next (nullptr) {} + ~hb_free_pool_t () { next = nullptr; @@ -104,4 +104,4 @@ struct hb_pool_t }; -#endif /* HB_POOL_HH */ +#endif /* HB_FREE_POOL_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc index e9c0e734a53..b90f966c57c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc @@ -143,6 +143,9 @@ _hb_ft_font_destroy (void *data) /* hb_font changed, update FT_Face. */ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) { + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; float x_mult = 1.f, y_mult = 1.f; @@ -184,12 +187,14 @@ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) FT_Set_Transform (ft_face, &matrix, nullptr); ft_font->transform = true; } + else + FT_Set_Transform (ft_face, nullptr, nullptr); #if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) - unsigned int num_coords; - const float *coords = hb_font_get_var_coords_design (font, &num_coords); - if (num_coords) + if (font->has_nonzero_coords) { + unsigned int num_coords; + const float *coords = hb_font_get_var_coords_design (font, &num_coords); FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed)); if (ft_coords) { @@ -199,6 +204,12 @@ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) hb_free (ft_coords); } } + else if (font->num_coords) + { + // Some old versions of FreeType crash if we + // call this function on non-variable fonts. + FT_Set_Var_Design_Coordinates (ft_face, 0, nullptr); + } #endif } @@ -1093,6 +1104,10 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data FT_ULong length = 0; FT_Error error; + /* In new FreeType, a tag value of 1 loads the SFNT table directory. Reject it. */ + if (tag == 1) + return nullptr; + /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length); @@ -1366,7 +1381,7 @@ hb_ft_font_changed (hb_font_t *font) for (unsigned int i = 0; i < mm_var->num_axis; ++i) { - coords[i] = ft_coords[i] >>= 2; + coords[i] = (ft_coords[i] + 2) >> 2; nonzero = nonzero || coords[i]; } @@ -1717,7 +1732,12 @@ hb_ft_font_set_funcs (hb_font_t *font) ft_face->generic.finalizer = _release_blob; // And the FT_Library to the blob - hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true); + if (unlikely (!hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true))) + { + DEBUG_MSG (FT, font, "hb_blob_set_user_data() failed"); + FT_Done_Face (ft_face); + return; + } _hb_ft_font_set_funcs (font, ft_face, true); hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh b/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh index 795f29f6ab0..0de062df68e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-geometry.hh @@ -26,7 +26,10 @@ #include "hb.hh" +#include "hb-algs.hh" + +template struct hb_extents_t { hb_extents_t () {} @@ -35,7 +38,7 @@ struct hb_extents_t ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)), xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)), ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {} - hb_extents_t (float xmin, float ymin, float xmax, float ymax) : + hb_extents_t (Float xmin, Float ymin, Float xmax, Float ymax) : xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} bool is_empty () const { return xmin >= xmax || ymin >= ymax; } @@ -69,7 +72,7 @@ struct hb_extents_t } void - add_point (float x, float y) + add_point (Float x, Float y) { if (unlikely (is_void ())) { @@ -97,62 +100,69 @@ struct hb_extents_t yneg ? y1 - y0 : y0 - y1}; } - float xmin = 0.f; - float ymin = 0.f; - float xmax = -1.f; - float ymax = -1.f; + Float xmin = 0; + Float ymin = 0; + Float xmax = -1; + Float ymax = -1; }; +template struct hb_transform_t { hb_transform_t () {} - hb_transform_t (float xx, float yx, - float xy, float yy, - float x0, float y0) : + hb_transform_t (Float xx, Float yx, + Float xy, Float yy, + Float x0, Float y0) : xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} bool is_identity () const { - return xx == 1.f && yx == 0.f && - xy == 0.f && yy == 1.f && - x0 == 0.f && y0 == 0.f; + return xx == 1 && yx == 0 && + xy == 0 && yy == 1 && + x0 == 0 && y0 == 0; + } + bool is_translation () const + { + return xx == 1 && yx == 0 && + xy == 0 && yy == 1; } - void multiply (const hb_transform_t &o) + void multiply (const hb_transform_t &o, bool before=false) { - /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ - hb_transform_t r; - - r.xx = o.xx * xx + o.yx * xy; - r.yx = o.xx * yx + o.yx * yy; - - r.xy = o.xy * xx + o.yy * xy; - r.yy = o.xy * yx + o.yy * yy; - - r.x0 = o.x0 * xx + o.y0 * xy + x0; - r.y0 = o.x0 * yx + o.y0 * yy + y0; - - *this = r; + // Copied from cairo-matrix.c + const hb_transform_t &a = before ? o : *this; + const hb_transform_t &b = before ? *this : o; + *this = { + a.xx * b.xx + a.xy * b.yx, + a.yx * b.xx + a.yy * b.yx, + a.xx * b.xy + a.xy * b.yy, + a.yx * b.xy + a.yy * b.yy, + a.xx * b.x0 + a.xy * b.y0 + a.x0, + a.yx * b.x0 + a.yy * b.y0 + a.y0 + }; } - void transform_distance (float &dx, float &dy) const + HB_ALWAYS_INLINE + void transform_distance (Float &dx, Float &dy) const { - float new_x = xx * dx + xy * dy; - float new_y = yx * dx + yy * dy; + Float new_x = xx * dx + xy * dy; + Float new_y = yx * dx + yy * dy; dx = new_x; dy = new_y; } - void transform_point (float &x, float &y) const + HB_ALWAYS_INLINE + void transform_point (Float &x, Float &y) const { - transform_distance (x, y); - x += x0; - y += y0; + Float new_x = x0 + xx * x + xy * y; + Float new_y = y0 + yx * x + yy * y; + x = new_x; + y = new_y; } - void transform_extents (hb_extents_t &extents) const + void transform_extents (hb_extents_t &extents) const { - float quad_x[4], quad_y[4]; + Float quad_x[4], quad_y[4]; quad_x[0] = extents.xmin; quad_y[0] = extents.ymin; @@ -163,7 +173,7 @@ struct hb_transform_t quad_x[3] = extents.xmax; quad_y[3] = extents.ymax; - extents = hb_extents_t {}; + extents = hb_extents_t {}; for (unsigned i = 0; i < 4; i++) { transform_point (quad_x[i], quad_y[i]); @@ -171,20 +181,36 @@ struct hb_transform_t } } - void transform (const hb_transform_t &o) { multiply (o); } + void transform (const hb_transform_t &o, bool before=false) { multiply (o, before); } - void translate (float x, float y) + static hb_transform_t translation (Float x, Float y) { - if (x == 0.f && y == 0.f) - return; + return {1, 0, 0, 1, x, y}; + } + void translate (Float x, Float y, bool before=false) + { + if (before) + { + x0 += x; + y0 += y; + } + else + { + if (x == 0 && y == 0) + return; - x0 += xx * x + xy * y; - y0 += yx * x + yy * y; + x0 += xx * x + xy * y; + y0 += yx * x + yy * y; + } } - void scale (float scaleX, float scaleY) + static hb_transform_t scaling (Float scaleX, Float scaleY) { - if (scaleX == 1.f && scaleY == 1.f) + return {scaleX, 0, 0, scaleY, 0, 0}; + } + void scale (Float scaleX, Float scaleY) + { + if (scaleX == 1 && scaleY == 1) return; xx *= scaleX; @@ -192,52 +218,94 @@ struct hb_transform_t xy *= scaleY; yy *= scaleY; } - - void rotate (float rotation) + static hb_transform_t scaling_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) { - if (rotation == 0.f) + return {scaleX, 0, 0, scaleY, + center_x ? (1 - scaleX) * center_x : 0, + center_y ? (1 - scaleY) * center_y : 0}; + } + void scale_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y) + { + if (scaleX == 1 && scaleY == 1) return; + transform (scaling_around_center (scaleX, scaleY, center_x, center_y)); + } + + static hb_transform_t rotation (Float radians) + { // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240 - rotation = rotation * HB_PI; - float c; - float s; -#ifdef HAVE_SINCOSF - sincosf (rotation, &s, &c); -#else - c = cosf (rotation); - s = sinf (rotation); -#endif - auto other = hb_transform_t{c, s, -s, c, 0.f, 0.f}; - transform (other); + Float c; + Float s; + hb_sincos (radians, s, c); + return {c, s, -s, c, 0, 0}; } - - void skew (float skewX, float skewY) + void rotate (Float radians, bool before=false) { - if (skewX == 0.f && skewY == 0.f) + if (radians == 0) return; - // https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255 - skewX = skewX * HB_PI; - skewY = skewY * HB_PI; - auto other = hb_transform_t{1.f, - skewY ? tanf (skewY) : 0.f, - skewX ? tanf (skewX) : 0.f, - 1.f, - 0.f, 0.f}; - transform (other); + transform (rotation (radians), before); } - float xx = 1.f; - float yx = 0.f; - float xy = 0.f; - float yy = 1.f; - float x0 = 0.f; - float y0 = 0.f; + static hb_transform_t rotation_around_center (Float radians, Float center_x, Float center_y) + { + Float s, c; + hb_sincos (radians, s, c); + return { + c, s, -s, c, + (1 - c) * center_x + s * center_y, + -s * center_x + (1 - c) * center_y + }; + } + void rotate_around_center (Float radians, Float center_x, Float center_y, bool before=false) + { + if (radians == 0) + return; + + transform (rotation_around_center (radians, center_x, center_y), before); + } + + static hb_transform_t skewing (Float skewX, Float skewY) + { + return {1, skewY ? tanf (skewY) : 0, skewX ? tanf (skewX) : 0, 1, 0, 0}; + } + void skew (Float skewX, Float skewY) + { + if (skewX == 0 && skewY == 0) + return; + + transform (skewing (skewX, skewY)); + } + static hb_transform_t skewing_around_center (Float skewX, Float skewY, Float center_x, Float center_y) + { + skewX = skewX ? tanf (skewX) : 0; + skewY = skewY ? tanf (skewY) : 0; + return { + 1, skewY, skewX, 1, + center_y ? -skewX * center_y : 0, + center_x ? -skewY * center_x : 0 + }; + } + void skew_around_center (Float skewX, Float skewY, Float center_x, Float center_y) + { + if (skewX == 0 && skewY == 0) + return; + + transform (skewing_around_center (skewX, skewY, center_x, center_y)); + } + + Float xx = 1; + Float yx = 0; + Float xy = 0; + Float yy = 1; + Float x0 = 0; + Float y0 = 0; }; -#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f} +#define HB_TRANSFORM_IDENTITY {1, 0, 0, 1, 0, 0} +template struct hb_bounds_t { enum status_t { @@ -247,7 +315,7 @@ struct hb_bounds_t }; hb_bounds_t (status_t status = UNBOUNDED) : status (status) {} - hb_bounds_t (const hb_extents_t &extents) : + hb_bounds_t (const hb_extents_t &extents) : status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} void union_ (const hb_bounds_t &o) @@ -281,20 +349,21 @@ struct hb_bounds_t } status_t status; - hb_extents_t extents; + hb_extents_t extents; }; +template struct hb_transform_decomposed_t { - float translateX = 0; - float translateY = 0; - float rotation = 0; // in degrees, counter-clockwise - float scaleX = 1; - float scaleY = 1; - float skewX = 0; // in degrees, counter-clockwise - float skewY = 0; // in degrees, counter-clockwise - float tCenterX = 0; - float tCenterY = 0; + Float translateX = 0; + Float translateY = 0; + Float rotation = 0; // in radians, counter-clockwise + Float scaleX = 1; + Float scaleY = 1; + Float skewX = 0; // in radians, counter-clockwise + Float skewY = 0; // in radians, counter-clockwise + Float tCenterX = 0; + Float tCenterY = 0; operator bool () const { @@ -305,9 +374,9 @@ struct hb_transform_decomposed_t tCenterX || tCenterY; } - hb_transform_t to_transform () const + hb_transform_t to_transform () const { - hb_transform_t t; + hb_transform_t t; t.translate (translateX + tCenterX, translateY + tCenterY); t.rotate (rotation); t.scale (scaleX, scaleY); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh index 21d9544a74b..661e6771a4a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh @@ -772,8 +772,9 @@ struct hb_iota_iter_t : template auto inc (hb_type_identity s, hb_priority<1>) - -> hb_void_t (s), hb_declval ()))> - { v = hb_invoke (std::forward (s), v); } + -> hb_void_t> (s), + hb_declval ()))> + { v = hb_invoke (std::forward> (s), v); } void inc (S s, hb_priority<0>) @@ -972,7 +973,7 @@ struct Proj&& f = hb_identity) const { for (auto it = hb_iter (c); it; ++it) - if (!hb_match (std::forward (p), hb_get (std::forward (f), *it))) + if (!hb_match (p, hb_get (f, *it))) return false; return true; } @@ -989,7 +990,7 @@ struct Proj&& f = hb_identity) const { for (auto it = hb_iter (c); it; ++it) - if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + if (hb_match (p, hb_get (f, *it))) return true; return false; } @@ -1006,7 +1007,7 @@ struct Proj&& f = hb_identity) const { for (auto it = hb_iter (c); it; ++it) - if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + if (hb_match (p, hb_get (f, *it))) return false; return true; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-kern.hh b/src/java.desktop/share/native/libharfbuzz/hb-kern.hh index 1f2c8d5811b..cf08c16689f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-kern.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-kern.hh @@ -70,7 +70,7 @@ struct hb_kern_machine_t continue; } - skippy_iter.reset (idx); + skippy_iter.reset_fast (idx); unsigned unsafe_to; if (!skippy_iter.next (&unsafe_to)) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-limits.hh b/src/java.desktop/share/native/libharfbuzz/hb-limits.hh index fbc7bbe764e..857e183737f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-limits.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-limits.hh @@ -29,20 +29,20 @@ #ifndef HB_BUFFER_MAX_LEN_FACTOR -#define HB_BUFFER_MAX_LEN_FACTOR 64 +#define HB_BUFFER_MAX_LEN_FACTOR 256 #endif #ifndef HB_BUFFER_MAX_LEN_MIN -#define HB_BUFFER_MAX_LEN_MIN 16384 +#define HB_BUFFER_MAX_LEN_MIN 65536 #endif #ifndef HB_BUFFER_MAX_LEN_DEFAULT #define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ #endif #ifndef HB_BUFFER_MAX_OPS_FACTOR -#define HB_BUFFER_MAX_OPS_FACTOR 1024 +#define HB_BUFFER_MAX_OPS_FACTOR 4096 #endif #ifndef HB_BUFFER_MAX_OPS_MIN -#define HB_BUFFER_MAX_OPS_MIN 16384 +#define HB_BUFFER_MAX_OPS_MIN 65536 #endif #ifndef HB_BUFFER_MAX_OPS_DEFAULT #define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh index ffbb8f3d301..8fa32ea8eaa 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh @@ -66,13 +66,22 @@ static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset) } /* StructAfter(X) returns the struct T& that is placed after X. - * Works with X of variable size also. X must implement get_size() */ -template -static inline const Type& StructAfter(const TObject &X) -{ return StructAtOffset(&X, X.get_size()); } -template -static inline Type& StructAfter(TObject &X) -{ return StructAtOffset(&X, X.get_size()); } + * Works with X of variable size also. X must implement get_size(). + * Any extra arguments are forwarded to get_size, so for example + * it can work with UnsizedArrayOf<> as well. */ +template +static inline auto StructAfter(const TObject &X, Ts... args) HB_AUTO_RETURN(( + StructAtOffset(&X, X.get_size(std::forward (args)...)) +)) +/* The is_const shenanigans is to avoid ambiguous overload with gcc-8. + * It disables this path when TObject is const. + * See: https://github.com/harfbuzz/harfbuzz/issues/5429 */ +template +static inline auto StructAfter(TObject &X, Ts... args) HB_AUTO_RETURN(( + sizeof(int[std::is_const::value ? -1 : +1]) > 0 ? + StructAtOffset(&X, X.get_size(std::forward (args)...)) + : *reinterpret_cast (0) +)) /* @@ -132,7 +141,6 @@ static inline Type& StructAfter(TObject &X) DEFINE_SIZE_ARRAY(size, array) - /* * Lazy loaders. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-map.hh index 76206342f57..4c76622df7a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.hh @@ -47,11 +47,11 @@ struct hb_hashmap_t hb_hashmap_t () { init (); } ~hb_hashmap_t () { fini (); } - hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () + void _copy (const hb_hashmap_t& o) { if (unlikely (!o.mask)) return; - if (item_t::is_trivial) + if (hb_is_trivially_copy_assignable (item_t)) { items = (item_t *) hb_malloc (sizeof (item_t) * (o.mask + 1)); if (unlikely (!items)) @@ -70,8 +70,16 @@ struct hb_hashmap_t alloc (o.population); hb_copy (o, *this); } + + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { _copy (o); } + hb_hashmap_t& operator= (const hb_hashmap_t& o) + { + reset (); + if (!items) { _copy (o); return *this; } + alloc (o.population); hb_copy (o, *this); return *this; + } + hb_hashmap_t (hb_hashmap_t&& o) noexcept : hb_hashmap_t () { hb_swap (*this, o); } - hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; } hb_hashmap_t& operator= (hb_hashmap_t&& o) noexcept { hb_swap (*this, o); return *this; } hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t () @@ -130,10 +138,7 @@ struct hb_hashmap_t uint32_t total_hash () const { return (hash * 31u) + hb_hash (value); } - static constexpr bool is_trivial = hb_is_trivially_constructible(K) && - hb_is_trivially_destructible(K) && - hb_is_trivially_constructible(V) && - hb_is_trivially_destructible(V); + static constexpr bool is_trivially_constructible = (hb_is_trivially_constructible(K) && hb_is_trivially_constructible(V)); }; hb_object_header_t header; @@ -174,19 +179,19 @@ struct hb_hashmap_t if (likely (items)) { unsigned size = mask + 1; - if (!item_t::is_trivial) - for (unsigned i = 0; i < size; i++) - items[i].~item_t (); + for (unsigned i = 0; i < size; i++) + items[i].~item_t (); hb_free (items); items = nullptr; } population = occupancy = 0; } - void reset () + hb_hashmap_t& reset () { successful = true; clear (); + return *this; } bool in_error () const { return !successful; } @@ -197,7 +202,7 @@ struct hb_hashmap_t if (new_population != 0 && (new_population + new_population / 2) < mask) return true; - unsigned int power = hb_bit_storage (hb_max ((unsigned) population, new_population) * 2 + 8); + unsigned int power = hb_bit_storage (hb_max (hb_max ((unsigned) population, new_population) * 2, 4u)); unsigned int new_size = 1u << power; item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t)); if (unlikely (!new_items)) @@ -205,7 +210,7 @@ struct hb_hashmap_t successful = false; return false; } - if (!item_t::is_trivial) + if (!item_t::is_trivially_constructible) for (auto &_ : hb_iter (new_items, new_size)) new (&_) item_t (); else @@ -231,9 +236,8 @@ struct hb_hashmap_t std::move (old_items[i].value)); } } - if (!item_t::is_trivial) - for (unsigned int i = 0; i < old_size; i++) - old_items[i].~item_t (); + for (unsigned int i = 0; i < old_size; i++) + old_items[i].~item_t (); hb_free (old_items); @@ -335,7 +339,13 @@ struct hb_hashmap_t bool has (const K &key, VV **vp = nullptr) const { if (!items) return false; - auto *item = fetch_item (key, hb_hash (key)); + return has_with_hash (key, hb_hash (key), vp); + } + template + bool has_with_hash (const K &key, uint32_t hash, VV **vp = nullptr) const + { + if (!items) return false; + auto *item = fetch_item (key, hash); if (item) { if (vp) *vp = std::addressof (item->value); @@ -481,10 +491,17 @@ struct hb_hashmap_t /* Sink interface. */ hb_hashmap_t& operator << (const hb_pair_t& v) { set (v.first, v.second); return *this; } + template hb_hashmap_t& operator << (const hb_pair_t& v) { set (v.first, std::move (v.second)); return *this; } + template hb_hashmap_t& operator << (const hb_pair_t& v) { set (std::move (v.first), v.second); return *this; } + template hb_hashmap_t& operator << (const hb_pair_t& v) { set (std::move (v.first), std::move (v.second)); return *this; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh index 9d2867e4835..07f3ebe0c7d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh @@ -31,7 +31,7 @@ #include "hb.hh" -#line 35 "hb-number-parser.hh" +#line 32 "hb-number-parser.hh" static const unsigned char _double_parser_trans_keys[] = { 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, 46u, 101u, 0 @@ -135,12 +135,12 @@ strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) int cs; -#line 139 "hb-number-parser.hh" +#line 132 "hb-number-parser.hh" { cs = double_parser_start; } -#line 144 "hb-number-parser.hh" +#line 135 "hb-number-parser.hh" { int _slen; int _trans; @@ -198,7 +198,7 @@ _resume: exp_overflow = true; } break; -#line 202 "hb-number-parser.hh" +#line 187 "hb-number-parser.hh" } _again: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh index 382ee2ddaf2..538240b6416 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh @@ -465,11 +465,11 @@ struct OpenTypeFontFile Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ }; - hb_tag_t get_tag () const { return u.tag; } + hb_tag_t get_tag () const { return u.tag.v; } unsigned int get_face_count () const { - switch (u.tag) { + switch (u.tag.v) { case CFFTag: /* All the non-collection tags */ case TrueTag: case Typ1Tag: @@ -483,7 +483,7 @@ struct OpenTypeFontFile { if (base_offset) *base_offset = 0; - switch (u.tag) { + switch (u.tag.v) { /* Note: for non-collection SFNT data we ignore index. This is because * Apple dfont container is a container of SFNT's. So each SFNT is a * non-TTC, but the index is more than zero. */ @@ -512,9 +512,9 @@ struct OpenTypeFontFile bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (!u.tag.sanitize (c))) return_trace (false); + if (unlikely (!u.tag.v.sanitize (c))) return_trace (false); hb_barrier (); - switch (u.tag) { + switch (u.tag.v) { case CFFTag: /* All the non-collection tags */ case TrueTag: case Typ1Tag: @@ -527,13 +527,13 @@ struct OpenTypeFontFile protected: union { - Tag tag; /* 4-byte identifier. */ + struct { Tag v; } tag; /* 4-byte identifier. */ OpenTypeFontFace fontFace; TTCHeader ttcHeader; ResourceForkHeader rfHeader; } u; public: - DEFINE_SIZE_UNION (4, tag); + DEFINE_SIZE_UNION (4, tag.v); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh index 75d87f11ea5..ad831b66d56 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh @@ -54,35 +54,41 @@ namespace OT { */ /* Integer types in big-endian order and no alignment requirement */ -template -struct IntType +struct NumType { typedef Type type; - - IntType () = default; - explicit constexpr IntType (Type V) : v {V} {} - IntType& operator = (Type i) { v = i; return *this; } /* For reason we define cast out operator for signed/unsigned, instead of Type, see: * https://github.com/harfbuzz/harfbuzz/pull/2875/commits/09836013995cab2b9f07577a179ad7b024130467 */ - operator typename std::conditional::value, signed, unsigned>::type () const { return v; } + typedef typename std::conditional::value && sizeof (Type) <= sizeof(int), + typename std::conditional::value, signed, unsigned>::type, + Type>::type WideType; - bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } - bool operator != (const IntType &o) const { return !(*this == o); } + NumType () = default; + explicit constexpr NumType (Type V) : v {V} {} + NumType& operator = (Type V) { v = V; return *this; } - IntType& operator += (unsigned count) { *this = *this + count; return *this; } - IntType& operator -= (unsigned count) { *this = *this - count; return *this; } - IntType& operator ++ () { *this += 1; return *this; } - IntType& operator -- () { *this -= 1; return *this; } - IntType operator ++ (int) { IntType c (*this); ++*this; return c; } - IntType operator -- (int) { IntType c (*this); --*this; return c; } + operator WideType () const { return v; } - HB_INTERNAL static int cmp (const IntType *a, const IntType *b) + bool operator == (const NumType &o) const { return (Type) v == (Type) o.v; } + bool operator != (const NumType &o) const { return !(*this == o); } + + NumType& operator += (WideType count) { *this = *this + count; return *this; } + NumType& operator -= (WideType count) { *this = *this - count; return *this; } + NumType& operator ++ () { *this += 1; return *this; } + NumType& operator -- () { *this -= 1; return *this; } + NumType operator ++ (int) { NumType c (*this); ++*this; return c; } + NumType operator -- (int) { NumType c (*this); --*this; return c; } + + uint32_t hash () const { return hb_array ((const char *) &v, sizeof (v)).hash (); } + HB_INTERNAL static int cmp (const NumType *a, const NumType *b) { return b->cmp (*a); } HB_INTERNAL static int cmp (const void *a, const void *b) { - IntType *pa = (IntType *) a; - IntType *pb = (IntType *) b; + NumType *pa = (NumType *) a; + NumType *pb = (NumType *) b; return pb->cmp (*pa); } @@ -99,20 +105,36 @@ struct IntType return_trace (c->check_struct (this)); } protected: - BEInt v; + typename std::conditional::value, + HBInt, + HBFloat>::type v; public: DEFINE_SIZE_STATIC (Size); }; -typedef IntType HBUINT8; /* 8-bit unsigned integer. */ -typedef IntType HBINT8; /* 8-bit signed integer. */ -typedef IntType HBUINT16; /* 16-bit unsigned integer. */ -typedef IntType HBINT16; /* 16-bit signed integer. */ -typedef IntType HBUINT32; /* 32-bit unsigned integer. */ -typedef IntType HBINT32; /* 32-bit signed integer. */ +typedef NumType HBUINT8; /* 8-bit big-endian unsigned integer. */ +typedef NumType HBINT8; /* 8-bit big-endian signed integer. */ +typedef NumType HBUINT16; /* 16-bit big-endian unsigned integer. */ +typedef NumType HBINT16; /* 16-bit big-endian signed integer. */ +typedef NumType HBUINT32; /* 32-bit big-endian unsigned integer. */ +typedef NumType HBINT32; /* 32-bit big-endian signed integer. */ +typedef NumType HBUINT64; /* 64-bit big-endian unsigned integer. */ +typedef NumType HBINT64; /* 64-bit big-endian signed integer. */ /* Note: we cannot defined a signed HBINT24 because there's no corresponding C type. * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */ -typedef IntType HBUINT24; /* 24-bit unsigned integer. */ +typedef NumType HBUINT24; /* 24-bit big-endian unsigned integer. */ + +typedef NumType HBUINT16LE; /* 16-bit little-endian unsigned integer. */ +typedef NumType HBINT16LE; /* 16-bit little-endian signed integer. */ +typedef NumType HBUINT32LE; /* 32-bit little-endian unsigned integer. */ +typedef NumType HBINT32LE; /* 32-bit little-endian signed integer. */ +typedef NumType HBUINT64LE; /* 64-bit little-endian unsigned integer. */ +typedef NumType HBINT64LE; /* 64-bit little-endian signed integer. */ + +typedef NumType HBFLOAT32BE; /* 32-bit little-endian floating point number. */ +typedef NumType HBFLOAT64BE; /* 64-bit little-endian floating point number. */ +typedef NumType HBFLOAT32LE; /* 32-bit little-endian floating point number. */ +typedef NumType HBFLOAT64LE; /* 64-bit little-endian floating point number. */ /* 15-bit unsigned number; top bit used for extension. */ struct HBUINT15 : HBUINT16 @@ -218,7 +240,7 @@ typedef HBUINT16 UFWORD; template struct HBFixed : Type { - static constexpr float shift = (float) (1 << fraction_bits); + static constexpr float mult = 1.f / (1 << fraction_bits); static_assert (Type::static_size * 8 > fraction_bits, ""); operator signed () const = delete; @@ -226,8 +248,8 @@ struct HBFixed : Type explicit operator float () const { return to_float (); } typename Type::type to_int () const { return Type::v; } void set_int (typename Type::type i ) { Type::v = i; } - float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) / shift; } - void set_float (float f) { Type::v = roundf (f * shift); } + float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) * mult; } + void set_float (float f) { Type::v = roundf (f / mult); } public: DEFINE_SIZE_STATIC (Type::static_size); }; @@ -504,16 +526,9 @@ struct OffsetTo : Offset return_trace (sanitize_shallow (c, base) && hb_barrier () && (this->is_null () || - c->dispatch (StructAtOffset (base, *this), std::forward (ds)...) || - neuter (c))); + c->dispatch (StructAtOffset (base, *this), std::forward (ds)...))); } - /* Set the offset to Null */ - bool neuter (hb_sanitize_context_t *c) const - { - if (!has_null) return false; - return c->try_set (this, 0); - } DEFINE_SIZE_STATIC (sizeof (OffsetType)); }; /* Partial specializations. */ @@ -1481,8 +1496,8 @@ struct TupleValues VALUE_RUN_COUNT_MASK = 0x3F }; - static unsigned compile (hb_array_t values, /* IN */ - hb_array_t encoded_bytes /* OUT */) + static unsigned compile_unsafe (hb_array_t values, /* IN */ + unsigned char *encoded_bytes /* OUT */) { unsigned num_values = values.length; unsigned encoded_len = 0; @@ -1491,24 +1506,23 @@ struct TupleValues { int val = values.arrayZ[i]; if (val == 0) - encoded_len += encode_value_run_as_zeroes (i, encoded_bytes.sub_array (encoded_len), values); - else if (val >= -128 && val <= 127) - encoded_len += encode_value_run_as_bytes (i, encoded_bytes.sub_array (encoded_len), values); - else if (val >= -32768 && val <= 32767) - encoded_len += encode_value_run_as_words (i, encoded_bytes.sub_array (encoded_len), values); + encoded_len += encode_value_run_as_zeroes (i, encoded_bytes + encoded_len, values); + else if ((int8_t) val == val) + encoded_len += encode_value_run_as_bytes (i, encoded_bytes + encoded_len, values); + else if ((int16_t) val == val) + encoded_len += encode_value_run_as_words (i, encoded_bytes + encoded_len, values); else - encoded_len += encode_value_run_as_longs (i, encoded_bytes.sub_array (encoded_len), values); + encoded_len += encode_value_run_as_longs (i, encoded_bytes + encoded_len, values); } return encoded_len; } static unsigned encode_value_run_as_zeroes (unsigned& i, - hb_array_t encoded_bytes, + unsigned char *it, hb_array_t values) { unsigned num_values = values.length; unsigned run_length = 0; - auto it = encoded_bytes.iter (); unsigned encoded_len = 0; while (i < num_values && values.arrayZ[i] == 0) { @@ -1532,7 +1546,7 @@ struct TupleValues } static unsigned encode_value_run_as_bytes (unsigned &i, - hb_array_t encoded_bytes, + unsigned char *it, hb_array_t values) { unsigned start = i; @@ -1540,7 +1554,7 @@ struct TupleValues while (i < num_values) { int val = values.arrayZ[i]; - if (val > 127 || val < -128) + if ((int8_t) val != val) break; /* from fonttools: if there're 2 or more zeros in a sequence, @@ -1553,7 +1567,6 @@ struct TupleValues unsigned run_length = i - start; unsigned encoded_len = 0; - auto it = encoded_bytes.iter (); while (run_length >= 64) { @@ -1561,10 +1574,9 @@ struct TupleValues encoded_len++; for (unsigned j = 0; j < 64; j++) - { - *it++ = static_cast (values.arrayZ[start + j]); - encoded_len++; - } + it[j] = static_cast (values.arrayZ[start + j]); + it += 64; + encoded_len += 64; start += 64; run_length -= 64; @@ -1575,18 +1587,16 @@ struct TupleValues *it++ = (VALUES_ARE_BYTES | (run_length - 1)); encoded_len++; - while (start < i) - { - *it++ = static_cast (values.arrayZ[start++]); - encoded_len++; - } + for (unsigned j = 0; j < run_length; j++) + it[j] = static_cast (values.arrayZ[start + j]); + encoded_len += run_length; } return encoded_len; } static unsigned encode_value_run_as_words (unsigned &i, - hb_array_t encoded_bytes, + unsigned char *it, hb_array_t values) { unsigned start = i; @@ -1595,22 +1605,24 @@ struct TupleValues { int val = values.arrayZ[i]; - /* start a new run for a single zero value*/ + if ((int16_t) val != val) + break; + + /* start a new run for a single zero value. */ if (val == 0) break; - /* from fonttools: continue word-encoded run if there's only one + /* From fonttools: continue word-encoded run if there's only one * single value in the range [-128, 127] because it is more compact. * Only start a new run when there're 2 continuous such values. */ - if (val >= -128 && val <= 127 && + if ((int8_t) val == val && i + 1 < num_values && - values.arrayZ[i+1] >= -128 && values.arrayZ[i+1] <= 127) + (int8_t) values.arrayZ[i+1] == values.arrayZ[i+1]) break; i++; } unsigned run_length = i - start; - auto it = encoded_bytes.iter (); unsigned encoded_len = 0; while (run_length >= 64) { @@ -1647,7 +1659,7 @@ struct TupleValues } static unsigned encode_value_run_as_longs (unsigned &i, - hb_array_t encoded_bytes, + unsigned char *it, hb_array_t values) { unsigned start = i; @@ -1656,14 +1668,13 @@ struct TupleValues { int val = values.arrayZ[i]; - if (val >= -32768 && val <= 32767) + if ((int16_t) val == val) break; i++; } unsigned run_length = i - start; - auto it = encoded_bytes.iter (); unsigned encoded_len = 0; while (run_length >= 64) { @@ -1704,10 +1715,14 @@ struct TupleValues } template +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif static bool decompile (const HBUINT8 *&p /* IN/OUT */, hb_vector_t &values /* IN/OUT */, const HBUINT8 *end, - bool consume_all = false) + bool consume_all = false, + unsigned start = 0) { unsigned i = 0; unsigned count = consume_all ? UINT_MAX : values.length; @@ -1720,19 +1735,24 @@ struct TupleValues unsigned run_count = (control & VALUE_RUN_COUNT_MASK) + 1; if (consume_all) { - if (unlikely (!values.resize (values.length + run_count, false))) + if (unlikely (!values.resize_dirty (values.length + run_count))) return false; } unsigned stop = i + run_count; if (unlikely (stop > count)) return false; + + unsigned skip = i < start ? hb_min (start - i, run_count) : 0; + i += skip; + if ((control & VALUES_SIZE_MASK) == VALUES_ARE_ZEROS) { - for (; i < stop; i++) - values.arrayZ[i] = 0; + hb_memset (&values.arrayZ[i], 0, (stop - i) * sizeof (T)); + i = stop; } else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_WORDS) { if (unlikely (p + run_count * HBINT16::static_size > end)) return false; + p += skip * HBINT16::static_size; #ifndef HB_OPTIMIZE_SIZE for (; i + 3 < stop; i += 4) { @@ -1755,6 +1775,7 @@ struct TupleValues else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_LONGS) { if (unlikely (p + run_count * HBINT32::static_size > end)) return false; + p += skip * HBINT32::static_size; for (; i < stop; i++) { values.arrayZ[i] = * (const HBINT32 *) p; @@ -1764,6 +1785,7 @@ struct TupleValues else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_BYTES) { if (unlikely (p + run_count > end)) return false; + p += skip * HBINT8::static_size; #ifndef HB_OPTIMIZE_SIZE for (; i + 3 < stop; i += 4) { @@ -1784,7 +1806,7 @@ struct TupleValues { iter_t (const unsigned char *p_, unsigned len_) : p (p_), endp (p_ + len_) - { if (ensure_run ()) read_value (); } + { if (likely (ensure_run ())) read_value (); } private: const unsigned char *p; @@ -1793,10 +1815,14 @@ struct TupleValues signed run_count = 0; unsigned width = 0; + HB_ALWAYS_INLINE bool ensure_run () { if (likely (run_count > 0)) return true; - + return _ensure_run (); + } + bool _ensure_run () + { if (unlikely (p >= endp)) { run_count = 0; @@ -1886,10 +1912,15 @@ struct TupleValues signed run_count = 0; unsigned width = 0; + HB_ALWAYS_INLINE bool ensure_run () { - if (run_count > 0) return true; + if (likely (run_count > 0)) return true; + return _ensure_run (); + } + bool _ensure_run () + { if (unlikely (p >= end)) { run_count = 0; @@ -2013,7 +2044,10 @@ struct TupleValues } #ifndef HB_OPTIMIZE_SIZE - if (scale == 1.0f) + // The following branch is supposed to speed things up by avoiding + // the multiplication in _add_to<> if scale is 1.0f. + // But in practice it seems to bloat the code and slow things down. + if (false && scale == 1.0f) _add_to (out); else #endif @@ -2038,6 +2072,23 @@ struct TupleList : CFF2Index }; +// Alignment + +template +struct Align +{ + unsigned get_size (const void *base) const + { + unsigned offset = (const char *) this - (const char *) base; + return (alignment - offset) & (alignment - 1); + } + + public: + DEFINE_SIZE_MIN (0); +}; + + + } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh index 1e3e0be5106..ec018efe645 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh @@ -79,7 +79,7 @@ struct Dict : UnsizedByteStr { TRACE_SERIALIZE (this); for (unsigned int i = 0; i < dictval.get_count (); i++) - if (unlikely (!opszr.serialize (c, dictval[i], std::forward (ds)...))) + if (unlikely (!opszr.serialize (c, dictval[i], ds...))) return_trace (false); return_trace (true); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh index 65d56ae18b5..bf56abb975c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh @@ -30,396 +30,396 @@ #include "hb.hh" #endif -_S(".notdef") -_S("space") -_S("exclam") -_S("quotedbl") -_S("numbersign") -_S("dollar") -_S("percent") -_S("ampersand") -_S("quoteright") -_S("parenleft") -_S("parenright") -_S("asterisk") -_S("plus") -_S("comma") -_S("hyphen") -_S("period") -_S("slash") -_S("zero") -_S("one") -_S("two") -_S("three") -_S("four") -_S("five") -_S("six") -_S("seven") -_S("eight") -_S("nine") -_S("colon") -_S("semicolon") -_S("less") -_S("equal") -_S("greater") -_S("question") -_S("at") -_S("A") -_S("B") -_S("C") -_S("D") -_S("E") -_S("F") -_S("G") -_S("H") -_S("I") -_S("J") -_S("K") -_S("L") -_S("M") -_S("N") -_S("O") -_S("P") -_S("Q") -_S("R") -_S("S") -_S("T") -_S("U") -_S("V") -_S("W") -_S("X") -_S("Y") -_S("Z") -_S("bracketleft") -_S("backslash") -_S("bracketright") -_S("asciicircum") -_S("underscore") -_S("quoteleft") -_S("a") -_S("b") -_S("c") -_S("d") -_S("e") -_S("f") -_S("g") -_S("h") -_S("i") -_S("j") -_S("k") -_S("l") -_S("m") -_S("n") -_S("o") -_S("p") -_S("q") -_S("r") -_S("s") -_S("t") -_S("u") -_S("v") -_S("w") -_S("x") -_S("y") -_S("z") -_S("braceleft") -_S("bar") -_S("braceright") -_S("asciitilde") -_S("exclamdown") -_S("cent") -_S("sterling") -_S("fraction") -_S("yen") -_S("florin") -_S("section") -_S("currency") -_S("quotesingle") -_S("quotedblleft") -_S("guillemotleft") -_S("guilsinglleft") -_S("guilsinglright") -_S("fi") -_S("fl") -_S("endash") -_S("dagger") -_S("daggerdbl") -_S("periodcentered") -_S("paragraph") -_S("bullet") -_S("quotesinglbase") -_S("quotedblbase") -_S("quotedblright") -_S("guillemotright") -_S("ellipsis") -_S("perthousand") -_S("questiondown") -_S("grave") -_S("acute") -_S("circumflex") -_S("tilde") -_S("macron") -_S("breve") -_S("dotaccent") -_S("dieresis") -_S("ring") -_S("cedilla") -_S("hungarumlaut") -_S("ogonek") -_S("caron") -_S("emdash") -_S("AE") -_S("ordfeminine") -_S("Lslash") -_S("Oslash") -_S("OE") -_S("ordmasculine") -_S("ae") -_S("dotlessi") -_S("lslash") -_S("oslash") -_S("oe") -_S("germandbls") -_S("onesuperior") -_S("logicalnot") -_S("mu") -_S("trademark") -_S("Eth") -_S("onehalf") -_S("plusminus") -_S("Thorn") -_S("onequarter") -_S("divide") -_S("brokenbar") -_S("degree") -_S("thorn") -_S("threequarters") -_S("twosuperior") -_S("registered") -_S("minus") -_S("eth") -_S("multiply") -_S("threesuperior") -_S("copyright") -_S("Aacute") -_S("Acircumflex") -_S("Adieresis") -_S("Agrave") -_S("Aring") -_S("Atilde") -_S("Ccedilla") -_S("Eacute") -_S("Ecircumflex") -_S("Edieresis") -_S("Egrave") -_S("Iacute") -_S("Icircumflex") -_S("Idieresis") -_S("Igrave") -_S("Ntilde") -_S("Oacute") -_S("Ocircumflex") -_S("Odieresis") -_S("Ograve") -_S("Otilde") -_S("Scaron") -_S("Uacute") -_S("Ucircumflex") -_S("Udieresis") -_S("Ugrave") -_S("Yacute") -_S("Ydieresis") -_S("Zcaron") -_S("aacute") -_S("acircumflex") -_S("adieresis") -_S("agrave") -_S("aring") -_S("atilde") -_S("ccedilla") -_S("eacute") -_S("ecircumflex") -_S("edieresis") -_S("egrave") -_S("iacute") -_S("icircumflex") -_S("idieresis") -_S("igrave") -_S("ntilde") -_S("oacute") -_S("ocircumflex") -_S("odieresis") -_S("ograve") -_S("otilde") -_S("scaron") -_S("uacute") -_S("ucircumflex") -_S("udieresis") -_S("ugrave") -_S("yacute") -_S("ydieresis") -_S("zcaron") -_S("exclamsmall") -_S("Hungarumlautsmall") -_S("dollaroldstyle") -_S("dollarsuperior") -_S("ampersandsmall") -_S("Acutesmall") -_S("parenleftsuperior") -_S("parenrightsuperior") -_S("twodotenleader") -_S("onedotenleader") -_S("zerooldstyle") -_S("oneoldstyle") -_S("twooldstyle") -_S("threeoldstyle") -_S("fouroldstyle") -_S("fiveoldstyle") -_S("sixoldstyle") -_S("sevenoldstyle") -_S("eightoldstyle") -_S("nineoldstyle") -_S("commasuperior") -_S("threequartersemdash") -_S("periodsuperior") -_S("questionsmall") -_S("asuperior") -_S("bsuperior") -_S("centsuperior") -_S("dsuperior") -_S("esuperior") -_S("isuperior") -_S("lsuperior") -_S("msuperior") -_S("nsuperior") -_S("osuperior") -_S("rsuperior") -_S("ssuperior") -_S("tsuperior") -_S("ff") -_S("ffi") -_S("ffl") -_S("parenleftinferior") -_S("parenrightinferior") -_S("Circumflexsmall") -_S("hyphensuperior") -_S("Gravesmall") -_S("Asmall") -_S("Bsmall") -_S("Csmall") -_S("Dsmall") -_S("Esmall") -_S("Fsmall") -_S("Gsmall") -_S("Hsmall") -_S("Ismall") -_S("Jsmall") -_S("Ksmall") -_S("Lsmall") -_S("Msmall") -_S("Nsmall") -_S("Osmall") -_S("Psmall") -_S("Qsmall") -_S("Rsmall") -_S("Ssmall") -_S("Tsmall") -_S("Usmall") -_S("Vsmall") -_S("Wsmall") -_S("Xsmall") -_S("Ysmall") -_S("Zsmall") -_S("colonmonetary") -_S("onefitted") -_S("rupiah") -_S("Tildesmall") -_S("exclamdownsmall") -_S("centoldstyle") -_S("Lslashsmall") -_S("Scaronsmall") -_S("Zcaronsmall") -_S("Dieresissmall") -_S("Brevesmall") -_S("Caronsmall") -_S("Dotaccentsmall") -_S("Macronsmall") -_S("figuredash") -_S("hypheninferior") -_S("Ogoneksmall") -_S("Ringsmall") -_S("Cedillasmall") -_S("questiondownsmall") -_S("oneeighth") -_S("threeeighths") -_S("fiveeighths") -_S("seveneighths") -_S("onethird") -_S("twothirds") -_S("zerosuperior") -_S("foursuperior") -_S("fivesuperior") -_S("sixsuperior") -_S("sevensuperior") -_S("eightsuperior") -_S("ninesuperior") -_S("zeroinferior") -_S("oneinferior") -_S("twoinferior") -_S("threeinferior") -_S("fourinferior") -_S("fiveinferior") -_S("sixinferior") -_S("seveninferior") -_S("eightinferior") -_S("nineinferior") -_S("centinferior") -_S("dollarinferior") -_S("periodinferior") -_S("commainferior") -_S("Agravesmall") -_S("Aacutesmall") -_S("Acircumflexsmall") -_S("Atildesmall") -_S("Adieresissmall") -_S("Aringsmall") -_S("AEsmall") -_S("Ccedillasmall") -_S("Egravesmall") -_S("Eacutesmall") -_S("Ecircumflexsmall") -_S("Edieresissmall") -_S("Igravesmall") -_S("Iacutesmall") -_S("Icircumflexsmall") -_S("Idieresissmall") -_S("Ethsmall") -_S("Ntildesmall") -_S("Ogravesmall") -_S("Oacutesmall") -_S("Ocircumflexsmall") -_S("Otildesmall") -_S("Odieresissmall") -_S("OEsmall") -_S("Oslashsmall") -_S("Ugravesmall") -_S("Uacutesmall") -_S("Ucircumflexsmall") -_S("Udieresissmall") -_S("Yacutesmall") -_S("Thornsmall") -_S("Ydieresissmall") -_S("001.000") -_S("001.001") -_S("001.002") -_S("001.003") -_S("Black") -_S("Bold") -_S("Book") -_S("Light") -_S("Medium") -_S("Regular") -_S("Roman") -_S("Semibold") +HB_STR(".notdef") +HB_STR("space") +HB_STR("exclam") +HB_STR("quotedbl") +HB_STR("numbersign") +HB_STR("dollar") +HB_STR("percent") +HB_STR("ampersand") +HB_STR("quoteright") +HB_STR("parenleft") +HB_STR("parenright") +HB_STR("asterisk") +HB_STR("plus") +HB_STR("comma") +HB_STR("hyphen") +HB_STR("period") +HB_STR("slash") +HB_STR("zero") +HB_STR("one") +HB_STR("two") +HB_STR("three") +HB_STR("four") +HB_STR("five") +HB_STR("six") +HB_STR("seven") +HB_STR("eight") +HB_STR("nine") +HB_STR("colon") +HB_STR("semicolon") +HB_STR("less") +HB_STR("equal") +HB_STR("greater") +HB_STR("question") +HB_STR("at") +HB_STR("A") +HB_STR("B") +HB_STR("C") +HB_STR("D") +HB_STR("E") +HB_STR("F") +HB_STR("G") +HB_STR("H") +HB_STR("I") +HB_STR("J") +HB_STR("K") +HB_STR("L") +HB_STR("M") +HB_STR("N") +HB_STR("O") +HB_STR("P") +HB_STR("Q") +HB_STR("R") +HB_STR("S") +HB_STR("T") +HB_STR("U") +HB_STR("V") +HB_STR("W") +HB_STR("X") +HB_STR("Y") +HB_STR("Z") +HB_STR("bracketleft") +HB_STR("backslash") +HB_STR("bracketright") +HB_STR("asciicircum") +HB_STR("underscore") +HB_STR("quoteleft") +HB_STR("a") +HB_STR("b") +HB_STR("c") +HB_STR("d") +HB_STR("e") +HB_STR("f") +HB_STR("g") +HB_STR("h") +HB_STR("i") +HB_STR("j") +HB_STR("k") +HB_STR("l") +HB_STR("m") +HB_STR("n") +HB_STR("o") +HB_STR("p") +HB_STR("q") +HB_STR("r") +HB_STR("s") +HB_STR("t") +HB_STR("u") +HB_STR("v") +HB_STR("w") +HB_STR("x") +HB_STR("y") +HB_STR("z") +HB_STR("braceleft") +HB_STR("bar") +HB_STR("braceright") +HB_STR("asciitilde") +HB_STR("exclamdown") +HB_STR("cent") +HB_STR("sterling") +HB_STR("fraction") +HB_STR("yen") +HB_STR("florin") +HB_STR("section") +HB_STR("currency") +HB_STR("quotesingle") +HB_STR("quotedblleft") +HB_STR("guillemotleft") +HB_STR("guilsinglleft") +HB_STR("guilsinglright") +HB_STR("fi") +HB_STR("fl") +HB_STR("endash") +HB_STR("dagger") +HB_STR("daggerdbl") +HB_STR("periodcentered") +HB_STR("paragraph") +HB_STR("bullet") +HB_STR("quotesinglbase") +HB_STR("quotedblbase") +HB_STR("quotedblright") +HB_STR("guillemotright") +HB_STR("ellipsis") +HB_STR("perthousand") +HB_STR("questiondown") +HB_STR("grave") +HB_STR("acute") +HB_STR("circumflex") +HB_STR("tilde") +HB_STR("macron") +HB_STR("breve") +HB_STR("dotaccent") +HB_STR("dieresis") +HB_STR("ring") +HB_STR("cedilla") +HB_STR("hungarumlaut") +HB_STR("ogonek") +HB_STR("caron") +HB_STR("emdash") +HB_STR("AE") +HB_STR("ordfeminine") +HB_STR("Lslash") +HB_STR("Oslash") +HB_STR("OE") +HB_STR("ordmasculine") +HB_STR("ae") +HB_STR("dotlessi") +HB_STR("lslash") +HB_STR("oslash") +HB_STR("oe") +HB_STR("germandbls") +HB_STR("onesuperior") +HB_STR("logicalnot") +HB_STR("mu") +HB_STR("trademark") +HB_STR("Eth") +HB_STR("onehalf") +HB_STR("plusminus") +HB_STR("Thorn") +HB_STR("onequarter") +HB_STR("divide") +HB_STR("brokenbar") +HB_STR("degree") +HB_STR("thorn") +HB_STR("threequarters") +HB_STR("twosuperior") +HB_STR("registered") +HB_STR("minus") +HB_STR("eth") +HB_STR("multiply") +HB_STR("threesuperior") +HB_STR("copyright") +HB_STR("Aacute") +HB_STR("Acircumflex") +HB_STR("Adieresis") +HB_STR("Agrave") +HB_STR("Aring") +HB_STR("Atilde") +HB_STR("Ccedilla") +HB_STR("Eacute") +HB_STR("Ecircumflex") +HB_STR("Edieresis") +HB_STR("Egrave") +HB_STR("Iacute") +HB_STR("Icircumflex") +HB_STR("Idieresis") +HB_STR("Igrave") +HB_STR("Ntilde") +HB_STR("Oacute") +HB_STR("Ocircumflex") +HB_STR("Odieresis") +HB_STR("Ograve") +HB_STR("Otilde") +HB_STR("Scaron") +HB_STR("Uacute") +HB_STR("Ucircumflex") +HB_STR("Udieresis") +HB_STR("Ugrave") +HB_STR("Yacute") +HB_STR("Ydieresis") +HB_STR("Zcaron") +HB_STR("aacute") +HB_STR("acircumflex") +HB_STR("adieresis") +HB_STR("agrave") +HB_STR("aring") +HB_STR("atilde") +HB_STR("ccedilla") +HB_STR("eacute") +HB_STR("ecircumflex") +HB_STR("edieresis") +HB_STR("egrave") +HB_STR("iacute") +HB_STR("icircumflex") +HB_STR("idieresis") +HB_STR("igrave") +HB_STR("ntilde") +HB_STR("oacute") +HB_STR("ocircumflex") +HB_STR("odieresis") +HB_STR("ograve") +HB_STR("otilde") +HB_STR("scaron") +HB_STR("uacute") +HB_STR("ucircumflex") +HB_STR("udieresis") +HB_STR("ugrave") +HB_STR("yacute") +HB_STR("ydieresis") +HB_STR("zcaron") +HB_STR("exclamsmall") +HB_STR("Hungarumlautsmall") +HB_STR("dollaroldstyle") +HB_STR("dollarsuperior") +HB_STR("ampersandsmall") +HB_STR("Acutesmall") +HB_STR("parenleftsuperior") +HB_STR("parenrightsuperior") +HB_STR("twodotenleader") +HB_STR("onedotenleader") +HB_STR("zerooldstyle") +HB_STR("oneoldstyle") +HB_STR("twooldstyle") +HB_STR("threeoldstyle") +HB_STR("fouroldstyle") +HB_STR("fiveoldstyle") +HB_STR("sixoldstyle") +HB_STR("sevenoldstyle") +HB_STR("eightoldstyle") +HB_STR("nineoldstyle") +HB_STR("commasuperior") +HB_STR("threequartersemdash") +HB_STR("periodsuperior") +HB_STR("questionsmall") +HB_STR("asuperior") +HB_STR("bsuperior") +HB_STR("centsuperior") +HB_STR("dsuperior") +HB_STR("esuperior") +HB_STR("isuperior") +HB_STR("lsuperior") +HB_STR("msuperior") +HB_STR("nsuperior") +HB_STR("osuperior") +HB_STR("rsuperior") +HB_STR("ssuperior") +HB_STR("tsuperior") +HB_STR("ff") +HB_STR("ffi") +HB_STR("ffl") +HB_STR("parenleftinferior") +HB_STR("parenrightinferior") +HB_STR("Circumflexsmall") +HB_STR("hyphensuperior") +HB_STR("Gravesmall") +HB_STR("Asmall") +HB_STR("Bsmall") +HB_STR("Csmall") +HB_STR("Dsmall") +HB_STR("Esmall") +HB_STR("Fsmall") +HB_STR("Gsmall") +HB_STR("Hsmall") +HB_STR("Ismall") +HB_STR("Jsmall") +HB_STR("Ksmall") +HB_STR("Lsmall") +HB_STR("Msmall") +HB_STR("Nsmall") +HB_STR("Osmall") +HB_STR("Psmall") +HB_STR("Qsmall") +HB_STR("Rsmall") +HB_STR("Ssmall") +HB_STR("Tsmall") +HB_STR("Usmall") +HB_STR("Vsmall") +HB_STR("Wsmall") +HB_STR("Xsmall") +HB_STR("Ysmall") +HB_STR("Zsmall") +HB_STR("colonmonetary") +HB_STR("onefitted") +HB_STR("rupiah") +HB_STR("Tildesmall") +HB_STR("exclamdownsmall") +HB_STR("centoldstyle") +HB_STR("Lslashsmall") +HB_STR("Scaronsmall") +HB_STR("Zcaronsmall") +HB_STR("Dieresissmall") +HB_STR("Brevesmall") +HB_STR("Caronsmall") +HB_STR("Dotaccentsmall") +HB_STR("Macronsmall") +HB_STR("figuredash") +HB_STR("hypheninferior") +HB_STR("Ogoneksmall") +HB_STR("Ringsmall") +HB_STR("Cedillasmall") +HB_STR("questiondownsmall") +HB_STR("oneeighth") +HB_STR("threeeighths") +HB_STR("fiveeighths") +HB_STR("seveneighths") +HB_STR("onethird") +HB_STR("twothirds") +HB_STR("zerosuperior") +HB_STR("foursuperior") +HB_STR("fivesuperior") +HB_STR("sixsuperior") +HB_STR("sevensuperior") +HB_STR("eightsuperior") +HB_STR("ninesuperior") +HB_STR("zeroinferior") +HB_STR("oneinferior") +HB_STR("twoinferior") +HB_STR("threeinferior") +HB_STR("fourinferior") +HB_STR("fiveinferior") +HB_STR("sixinferior") +HB_STR("seveninferior") +HB_STR("eightinferior") +HB_STR("nineinferior") +HB_STR("centinferior") +HB_STR("dollarinferior") +HB_STR("periodinferior") +HB_STR("commainferior") +HB_STR("Agravesmall") +HB_STR("Aacutesmall") +HB_STR("Acircumflexsmall") +HB_STR("Atildesmall") +HB_STR("Adieresissmall") +HB_STR("Aringsmall") +HB_STR("AEsmall") +HB_STR("Ccedillasmall") +HB_STR("Egravesmall") +HB_STR("Eacutesmall") +HB_STR("Ecircumflexsmall") +HB_STR("Edieresissmall") +HB_STR("Igravesmall") +HB_STR("Iacutesmall") +HB_STR("Icircumflexsmall") +HB_STR("Idieresissmall") +HB_STR("Ethsmall") +HB_STR("Ntildesmall") +HB_STR("Ogravesmall") +HB_STR("Oacutesmall") +HB_STR("Ocircumflexsmall") +HB_STR("Otildesmall") +HB_STR("Odieresissmall") +HB_STR("OEsmall") +HB_STR("Oslashsmall") +HB_STR("Ugravesmall") +HB_STR("Uacutesmall") +HB_STR("Ucircumflexsmall") +HB_STR("Udieresissmall") +HB_STR("Yacutesmall") +HB_STR("Thornsmall") +HB_STR("Ydieresissmall") +HB_STR("001.000") +HB_STR("001.001") +HB_STR("001.002") +HB_STR("001.003") +HB_STR("Black") +HB_STR("Bold") +HB_STR("Book") +HB_STR("Light") +HB_STR("Medium") +HB_STR("Regular") +HB_STR("Roman") +HB_STR("Semibold") #endif /* HB_OT_CFF1_STD_STR_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh index 27c4d31b9ce..778167eeb23 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh @@ -326,7 +326,7 @@ struct Charset0 void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const { - mapping->resize (num_glyphs, false); + mapping->resize_dirty (num_glyphs); for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++) mapping->arrayZ[gid] = {sids[gid - 1], gid}; } @@ -426,7 +426,7 @@ struct Charset1_2 { void collect_glyph_to_sid_map (glyph_to_sid_map_t *mapping, unsigned int num_glyphs) const { - mapping->resize (num_glyphs, false); + mapping->resize_dirty (num_glyphs); hb_codepoint_t gid = 1; if (gid >= num_glyphs) return; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc index b2702106ecc..499bd942cde 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc @@ -202,7 +202,11 @@ struct cff2_cs_opset_path_t : cff2_cs_opset_tcoords, font->num_coords)); + return get_path_at (font, + glyph, + draw_session, + hb_array (font->coords, + font->has_nonzero_coords ? font->num_coords : 0)); } bool OT::cff2::accelerator_t::get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t coords) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh index 6bf37c062fc..93e9c0a2697 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh @@ -501,10 +501,6 @@ struct CmapSubtableFormat4 this->length = c->length () - table_initpos; if ((long long) this->length != (long long) c->length () - table_initpos) { - // Length overflowed. Discard the current object before setting the error condition, otherwise - // discard is a noop which prevents the higher level code from reverting the serializer to the - // pre-error state in cmap4 overflow handling code. - c->pop_discard (); c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW); return; } @@ -701,16 +697,7 @@ struct CmapSubtableFormat4 hb_barrier (); if (unlikely (!c->check_range (this, length))) - { - /* Some broken fonts have too long of a "length" value. - * If that is the case, just change the value to truncate - * the subtable at the end of the blob. */ - uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, - (uintptr_t) (c->end - - (char *) this)); - if (!c->try_set (&length, new_length)) - return_trace (false); - } + return_trace (false); return_trace (16 + 4 * (unsigned int) segCountX2 <= length); } @@ -1500,7 +1487,7 @@ struct CmapSubtable bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0 .get_glyph (codepoint, glyph); case 4: hb_barrier (); return u.format4 .get_glyph (codepoint, glyph); case 6: hb_barrier (); return u.format6 .get_glyph (codepoint, glyph); @@ -1513,7 +1500,7 @@ struct CmapSubtable } void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0 .collect_unicodes (out); return; case 4: hb_barrier (); u.format4 .collect_unicodes (out); return; case 6: hb_barrier (); u.format6 .collect_unicodes (out); return; @@ -1529,7 +1516,7 @@ struct CmapSubtable hb_map_t *mapping, /* OUT */ unsigned num_glyphs = UINT_MAX) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0 .collect_mapping (unicodes, mapping); return; case 4: hb_barrier (); u.format4 .collect_mapping (unicodes, mapping); return; case 6: hb_barrier (); u.format6 .collect_mapping (unicodes, mapping); return; @@ -1543,7 +1530,7 @@ struct CmapSubtable unsigned get_language () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0 .get_language (); case 4: hb_barrier (); return u.format4 .get_language (); case 6: hb_barrier (); return u.format6 .get_language (); @@ -1574,9 +1561,9 @@ struct CmapSubtable bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0 .sanitize (c)); case 4: hb_barrier (); return_trace (u.format4 .sanitize (c)); case 6: hb_barrier (); return_trace (u.format6 .sanitize (c)); @@ -1590,7 +1577,7 @@ struct CmapSubtable public: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CmapSubtableFormat0 format0; CmapSubtableFormat4 format4; CmapSubtableFormat6 format6; @@ -1600,7 +1587,7 @@ struct CmapSubtable CmapSubtableFormat14 format14; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; @@ -1646,7 +1633,7 @@ struct EncodingRecord CmapSubtable *cmapsubtable = c->push (); unsigned origin_length = c->length (); cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); - if (c->length () - origin_length > 0) *objidx = c->pop_pack (); + if (c->length () - origin_length > 0 && !c->in_error()) *objidx = c->pop_pack (); else c->pop_discard (); } @@ -1683,6 +1670,10 @@ struct SubtableUnicodesCache { { SubtableUnicodesCache* cache = (SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache)); + + if (unlikely (!cache)) + return nullptr; + new (cache) SubtableUnicodesCache (source_table); return cache; } @@ -1776,6 +1767,10 @@ struct cmap ; SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table); + + if (unlikely (!cache)) + return nullptr; + for (const EncodingRecord& _ : it) cache->set_for(&_); // populate the cache for this encoding record. @@ -1810,7 +1805,7 @@ struct cmap if (c->in_error ()) return false; - unsigned format = (base+_.subtable).u.format; + unsigned format = (base+_.subtable).u.format.v; if (format != 4 && format != 12 && format != 14) continue; const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache); @@ -1912,7 +1907,7 @@ struct cmap + hb_iter (encodingRecord) | hb_map (&EncodingRecord::subtable) | hb_map (hb_add (this)) - | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format.v == 14; }) | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) ; } @@ -1937,7 +1932,7 @@ struct cmap for (const EncodingRecord& _ : encodingrec_iter) { - unsigned format = (this + _.subtable).u.format; + unsigned format = (this + _.subtable).u.format.v; if (format == 12) has_format12 = true; const EncodingRecord *table = std::addressof (_); @@ -2025,7 +2020,7 @@ struct cmap this->subtable_uvs = &Null (CmapSubtableFormat14); { const CmapSubtable *st = table->find_subtable (0, 5); - if (st && st->u.format == 14) + if (st && st->u.format.v == 14) subtable_uvs = &st->u.format14; } @@ -2069,7 +2064,7 @@ struct cmap else #endif { - switch (subtable->u.format) { + switch (subtable->u.format.v) { /* Accelerate format 4 and format 12. */ default: this->get_glyph_funcZ = get_glyph_from; @@ -2276,7 +2271,7 @@ struct cmap (_.platformID == 0 && _.encodingID == 4) || (_.platformID == 3 && _.encodingID == 1) || (_.platformID == 3 && _.encodingID == 10) || - (cmap + _.subtable).u.format == 14; + (cmap + _.subtable).u.format.v == 14; } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc index 0238bb346a9..8add9209f37 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc @@ -37,6 +37,7 @@ #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" +#include "hb-ot-var-gvar-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-hmtx-table.hh" @@ -64,18 +65,22 @@ using hb_ot_font_advance_cache_t = hb_cache_t<24, 16>; static_assert (sizeof (hb_ot_font_advance_cache_t) == 1024, ""); +using hb_ot_font_origin_cache_t = hb_cache_t<20, 20>; +static_assert (sizeof (hb_ot_font_origin_cache_t) == 1024, ""); + struct hb_ot_font_t { const hb_ot_face_t *ot_face; - /* h_advance caching */ + mutable hb_atomic_t cached_serial; mutable hb_atomic_t cached_coords_serial; - struct advance_cache_t + + struct direction_cache_t { mutable hb_atomic_t advance_cache; - mutable hb_atomic_t varStore_cache; + mutable hb_atomic_t varStore_cache; - ~advance_cache_t () + ~direction_cache_t () { clear (); } @@ -116,7 +121,7 @@ struct hb_ot_font_t goto retry; } - OT::ItemVariationStore::cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const + OT::hb_scalar_cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const { retry: auto *cache = varStore_cache.get_acquire (); @@ -127,7 +132,7 @@ struct hb_ot_font_t else goto retry; } - void release_varStore_cache (OT::ItemVariationStore::cache_t *cache) const + void release_varStore_cache (OT::hb_scalar_cache_t *cache) const { if (!cache) return; @@ -154,17 +159,157 @@ struct hb_ot_font_t } h, v; + struct origin_cache_t + { + mutable hb_atomic_t origin_cache; + mutable hb_atomic_t varStore_cache; + + ~origin_cache_t () + { + clear (); + } + + hb_ot_font_origin_cache_t *acquire_origin_cache () const + { + retry: + auto *cache = origin_cache.get_acquire (); + if (!cache) + { + cache = (hb_ot_font_origin_cache_t *) hb_malloc (sizeof (hb_ot_font_origin_cache_t)); + if (!cache) + return nullptr; + new (cache) hb_ot_font_origin_cache_t; + return cache; + } + if (origin_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_origin_cache (hb_ot_font_origin_cache_t *cache) const + { + if (!cache) + return; + if (!origin_cache.cmpexch (nullptr, cache)) + hb_free (cache); + } + void clear_origin_cache () const + { + retry: + auto *cache = origin_cache.get_acquire (); + if (!cache) + return; + if (origin_cache.cmpexch (cache, nullptr)) + hb_free (cache); + else + goto retry; + } + + OT::hb_scalar_cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const + { + retry: + auto *cache = varStore_cache.get_acquire (); + if (!cache) + return varStore.create_cache (); + if (varStore_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_varStore_cache (OT::hb_scalar_cache_t *cache) const + { + if (!cache) + return; + if (!varStore_cache.cmpexch (nullptr, cache)) + OT::ItemVariationStore::destroy_cache (cache); + } + void clear_varStore_cache () const + { + retry: + auto *cache = varStore_cache.get_acquire (); + if (!cache) + return; + if (varStore_cache.cmpexch (cache, nullptr)) + OT::ItemVariationStore::destroy_cache (cache); + else + goto retry; + } + + void clear () const + { + clear_origin_cache (); + clear_varStore_cache (); + } + } v_origin; + + struct draw_cache_t + { + mutable hb_atomic_t gvar_cache; + + ~draw_cache_t () + { + clear (); + } + + OT::hb_scalar_cache_t *acquire_gvar_cache (const OT::gvar_accelerator_t &gvar) const + { + retry: + auto *cache = gvar_cache.get_acquire (); + if (!cache) + return gvar.create_cache (); + if (gvar_cache.cmpexch (cache, nullptr)) + return cache; + else + goto retry; + } + void release_gvar_cache (OT::hb_scalar_cache_t *cache) const + { + if (!cache) + return; + if (!gvar_cache.cmpexch (nullptr, cache)) + OT::gvar_accelerator_t::destroy_cache (cache); + } + void clear_gvar_cache () const + { + retry: + auto *cache = gvar_cache.get_acquire (); + if (!cache) + return; + if (gvar_cache.cmpexch (cache, nullptr)) + OT::gvar_accelerator_t::destroy_cache (cache); + else + goto retry; + } + + void clear () const + { + clear_gvar_cache (); + } + } draw; + void check_serial (hb_font_t *font) const { int font_serial = font->serial_coords.get_acquire (); + if (cached_serial.get_acquire () != font_serial) + { + /* These caches are dependent on scale and synthetic settings. + * Any change to the font invalidates them. */ + v_origin.clear (); - if (cached_coords_serial.get_acquire () == font_serial) - return; + cached_serial.set_release (font_serial); + } - h.clear (); - v.clear (); + int font_serial_coords = font->serial_coords.get_acquire (); + if (cached_coords_serial.get_acquire () != font_serial_coords) + { + /* These caches are independent of scale or synthetic settings. + * Just variation changes will invalidate them. */ + h.clear (); + v.clear (); + draw.clear (); - cached_coords_serial.set_release (font_serial); + cached_coords_serial.set_release (font_serial_coords); + } } }; @@ -242,37 +387,59 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, unsigned advance_stride, void *user_data HB_UNUSED) { + // Duplicated in v_advances. Ugly. Keep in sync'ish. const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; - ot_font->check_serial (font); - const OT::HVAR &HVAR = *hmtx.var_table; - const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore; - OT::ItemVariationStore::cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore); - - hb_ot_font_advance_cache_t *advance_cache = nullptr; - - bool use_cache = font->num_coords; - if (use_cache) - { - advance_cache = ot_font->h.acquire_advance_cache (); - if (!advance_cache) - use_cache = false; - } - - if (!use_cache) + if (unlikely (!hmtx.has_data ())) { + hb_position_t advance = font->face->get_upem () / 2; + advance = font->em_scale_x (advance); for (unsigned int i = 0; i < count; i++) { - *first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + *first_advance = advance; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + +#ifndef HB_NO_VAR + if (!font->has_nonzero_coords) + { + fallback: +#else + { +#endif + // Just plain htmx data. No need to cache. + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_x (hmtx.get_advance_without_var_unscaled (*first_glyph)); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + return; } - else - { /* Use cache. */ + +#ifndef HB_NO_VAR + /* has_nonzero_coords. */ + + ot_font->check_serial (font); + hb_ot_font_advance_cache_t *advance_cache = ot_font->h.acquire_advance_cache (); + if (!advance_cache) + { + // malloc failure. Just use the fallback non-variable path. + goto fallback; + } + + /* If HVAR is present, use it.*/ + const OT::HVAR &HVAR = *hmtx.var_table; + if (HVAR.has_data ()) + { + const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore; + OT::hb_scalar_cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore); + for (unsigned int i = 0; i < count; i++) { hb_position_t v; @@ -289,10 +456,49 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + ot_font->h.release_varStore_cache (varStore_cache); ot_font->h.release_advance_cache (advance_cache); + return; } - ot_font->h.release_varStore_cache (varStore_cache); + const auto &gvar = *ot_face->gvar; + if (gvar.has_data ()) + { + const auto &glyf = *ot_face->glyf; + auto *scratch = glyf.acquire_scratch (); + if (unlikely (!scratch)) + { + ot_font->h.release_advance_cache (advance_cache); + goto fallback; + } + OT::hb_scalar_cache_t *gvar_cache = ot_font->draw.acquire_gvar_cache (gvar); + + for (unsigned int i = 0; i < count; i++) + { + hb_position_t v; + unsigned cv; + if (advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = glyf.get_advance_with_var_unscaled (*first_glyph, font, false, *scratch, gvar_cache); + advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_x (v); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + + ot_font->draw.release_gvar_cache (gvar_cache); + glyf.release_scratch (scratch); + ot_font->h.release_advance_cache (advance_cache); + return; + } + + ot_font->h.release_advance_cache (advance_cache); + // No HVAR or GVAR. Just use the fallback non-variable path. + goto fallback; +#endif } #ifndef HB_NO_VERTICAL @@ -305,99 +511,290 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, unsigned advance_stride, void *user_data HB_UNUSED) { + // Duplicated from h_advances. Ugly. Keep in sync'ish. + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - if (vmtx.has_data ()) + if (unlikely (!vmtx.has_data ())) + { + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t advance = font_extents.descender - font_extents.ascender; + for (unsigned int i = 0; i < count; i++) + { + *first_advance = advance; + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + +#ifndef HB_NO_VAR + if (!font->has_nonzero_coords) + { + fallback: +#else + { +#endif + // Just plain vtmx data. No need to cache. + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_y (- (int) vmtx.get_advance_without_var_unscaled (*first_glyph)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + +#ifndef HB_NO_VAR + /* has_nonzero_coords. */ + + ot_font->check_serial (font); + hb_ot_font_advance_cache_t *advance_cache = ot_font->v.acquire_advance_cache (); + if (!advance_cache) + { + // malloc failure. Just use the fallback non-variable path. + goto fallback; + } + + /* If VVAR is present, use it.*/ + const OT::VVAR &VVAR = *vmtx.var_table; + if (VVAR.has_data ()) { - ot_font->check_serial (font); - const OT::VVAR &VVAR = *vmtx.var_table; const OT::ItemVariationStore &varStore = &VVAR + VVAR.varStore; - OT::ItemVariationStore::cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore); - // TODO Use advance_cache. + OT::hb_scalar_cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore); for (unsigned int i = 0; i < count; i++) { - *first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + hb_position_t v; + unsigned cv; + if (advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache); + advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_y (- (int) v); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } ot_font->v.release_varStore_cache (varStore_cache); + ot_font->v.release_advance_cache (advance_cache); + return; } - else + + const auto &gvar = *ot_face->gvar; + if (gvar.has_data ()) { - hb_font_extents_t font_extents; - font->get_h_extents_with_fallback (&font_extents); - hb_position_t advance = -(font_extents.ascender - font_extents.descender); + const auto &glyf = *ot_face->glyf; + auto *scratch = glyf.acquire_scratch (); + if (unlikely (!scratch)) + { + ot_font->v.release_advance_cache (advance_cache); + goto fallback; + } + OT::hb_scalar_cache_t *gvar_cache = ot_font->draw.acquire_gvar_cache (gvar); for (unsigned int i = 0; i < count; i++) { - *first_advance = advance; + hb_position_t v; + unsigned cv; + if (advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = glyf.get_advance_with_var_unscaled (*first_glyph, font, true, *scratch, gvar_cache); + advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_y (- (int) v); first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); } + + ot_font->draw.release_gvar_cache (gvar_cache); + glyf.release_scratch (scratch); + ot_font->v.release_advance_cache (advance_cache); + return; } + + ot_font->v.release_advance_cache (advance_cache); + // No VVAR or GVAR. Just use the fallback non-variable path. + goto fallback; +#endif } #endif #ifndef HB_NO_VERTICAL +HB_HOT static hb_bool_t -hb_ot_get_glyph_v_origin (hb_font_t *font, - void *font_data, - hb_codepoint_t glyph, - hb_position_t *x, - hb_position_t *y, - void *user_data HB_UNUSED) +hb_ot_get_glyph_v_origins (hb_font_t *font, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_x, + unsigned x_stride, + hb_position_t *first_y, + unsigned y_stride, + void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_face_t *ot_face = ot_font->ot_face; - *x = font->get_glyph_h_advance (glyph) / 2; - - const OT::VORG &VORG = *ot_face->VORG; - if (VORG.has_data ()) + /* First, set all the x values to half the advance width. */ + font->get_glyph_h_advances (count, + first_glyph, glyph_stride, + first_x, x_stride); + for (unsigned i = 0; i < count; i++) { - float delta = 0; + *first_x /= 2; + first_x = &StructAtOffsetUnaligned (first_x, x_stride); + } + /* The vertical origin business is messy... + * + * We allocate the cache, then have various code paths that use the cache. + * Each one is responsible to free it before returning. + */ + hb_ot_font_origin_cache_t *origin_cache = ot_font->v_origin.acquire_origin_cache (); + + /* If there is VORG, always use it. It uses VVAR for variations if necessary. */ + const OT::VORG &VORG = *ot_face->VORG; + if (origin_cache && VORG.has_data ()) + { #ifndef HB_NO_VAR - const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - const OT::VVAR &VVAR = *vmtx.var_table; - if (font->num_coords) - VVAR.get_vorg_delta_unscaled (glyph, - font->coords, font->num_coords, - &delta); + if (!font->has_nonzero_coords) #endif + { + for (unsigned i = 0; i < count; i++) + { + hb_position_t origin; + unsigned cv; + if (origin_cache->get (*first_glyph, &cv)) + origin = font->y_scale < 0 ? -static_cast(cv) : static_cast(cv); + else + { + origin = font->em_scalef_y (VORG.get_y_origin (*first_glyph)); + origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin); + } - *y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta); + *first_y = origin; + + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } +#ifndef HB_NO_VAR + else + { + const OT::VVAR &VVAR = *ot_face->vmtx->var_table; + const auto &varStore = &VVAR + VVAR.varStore; + auto *varStore_cache = ot_font->v_origin.acquire_varStore_cache (varStore); + for (unsigned i = 0; i < count; i++) + { + hb_position_t origin; + unsigned cv; + if (origin_cache->get (*first_glyph, &cv)) + origin = font->y_scale < 0 ? -static_cast(cv) : static_cast(cv); + else + { + origin = font->em_scalef_y (VORG.get_y_origin (*first_glyph) + + VVAR.get_vorg_delta_unscaled (*first_glyph, + font->coords, font->num_coords, + varStore_cache)); + origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin); + } + + *first_y = origin; + + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + ot_font->v_origin.release_varStore_cache (varStore_cache); + } +#endif + ot_font->v_origin.release_origin_cache (origin_cache); return true; } - hb_glyph_extents_t extents = {0}; - - if (hb_font_get_glyph_extents (font, glyph, &extents)) + /* If and only if `vmtx` is present and it's a `glyf` font, + * we use the top phantom point, deduced from vmtx,glyf[,gvar]. */ + const auto &vmtx = *ot_face->vmtx; + const auto &glyf = *ot_face->glyf; + if (origin_cache && vmtx.has_data() && glyf.has_data ()) { - const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - int tsb = 0; - if (vmtx.get_leading_bearing_with_var_unscaled (font, glyph, &tsb)) + auto *scratch = glyf.acquire_scratch (); + if (unlikely (!scratch)) { - *y = extents.y_bearing + font->em_scale_y (tsb); - return true; + ot_font->v_origin.release_origin_cache (origin_cache); + return false; + } + OT::hb_scalar_cache_t *gvar_cache = font->has_nonzero_coords ? + ot_font->draw.acquire_gvar_cache (*ot_face->gvar) : + nullptr; + + for (unsigned i = 0; i < count; i++) + { + hb_position_t origin; + unsigned cv; + if (origin_cache->get (*first_glyph, &cv)) + origin = font->y_scale < 0 ? -static_cast(cv) : static_cast(cv); + else + { + origin = font->em_scalef_y (glyf.get_v_origin_with_var_unscaled (*first_glyph, font, *scratch, gvar_cache)); + origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin); + } + + *first_y = origin; + + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); } - hb_font_extents_t font_extents; - font->get_h_extents_with_fallback (&font_extents); - hb_position_t advance = font_extents.ascender - font_extents.descender; - hb_position_t diff = advance - -extents.height; - *y = extents.y_bearing + (diff >> 1); + if (gvar_cache) + ot_font->draw.release_gvar_cache (gvar_cache); + glyf.release_scratch (scratch); + ot_font->v_origin.release_origin_cache (origin_cache); return true; } - hb_font_extents_t font_extents; - font->get_h_extents_with_fallback (&font_extents); - *y = font_extents.ascender; + /* Otherwise, use glyph extents to center the glyph vertically. + * If getting glyph extents failed, just use the font ascender. */ + if (origin_cache && font->has_glyph_extents_func ()) + { + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t font_advance = font_extents.ascender - font_extents.descender; + for (unsigned i = 0; i < count; i++) + { + hb_position_t origin; + unsigned cv; + + if (origin_cache->get (*first_glyph, &cv)) + origin = font->y_scale < 0 ? -static_cast(cv) : static_cast(cv); + else + { + hb_glyph_extents_t extents = {0}; + if (likely (font->get_glyph_extents (*first_glyph, &extents))) + origin = extents.y_bearing + ((font_advance - -extents.height) >> 1); + else + origin = font_extents.ascender; + + origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin); + } + + *first_y = origin; + + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_y = &StructAtOffsetUnaligned (first_y, y_stride); + } + } + + ot_font->v_origin.release_origin_cache (origin_cache); return true; } #endif @@ -498,17 +895,33 @@ hb_ot_draw_glyph_or_fail (hb_font_t *font, hb_draw_funcs_t *draw_funcs, void *draw_data, void *user_data) { + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; hb_draw_session_t draw_session {draw_funcs, draw_data}; + bool ret = false; + + OT::hb_scalar_cache_t *gvar_cache = nullptr; + if (font->num_coords) + { + ot_font->check_serial (font); + gvar_cache = ot_font->draw.acquire_gvar_cache (*ot_font->ot_face->gvar); + } + #ifndef HB_NO_VAR_COMPOSITES - if (font->face->table.VARC->get_path (font, glyph, draw_session)) return true; + if (font->face->table.VARC->get_path (font, glyph, draw_session)) { ret = true; goto done; } #endif // Keep the following in synch with VARC::get_path_at() - if (font->face->table.glyf->get_path (font, glyph, draw_session)) return true; + if (font->face->table.glyf->get_path (font, glyph, draw_session, gvar_cache)) { ret = true; goto done; } + #ifndef HB_NO_CFF - if (font->face->table.cff2->get_path (font, glyph, draw_session)) return true; - if (font->face->table.cff1->get_path (font, glyph, draw_session)) return true; + if (font->face->table.cff2->get_path (font, glyph, draw_session)) { ret = true; goto done; } + if (font->face->table.cff1->get_path (font, glyph, draw_session)) { ret = true; goto done; } #endif - return false; + +done: + + ot_font->draw.release_gvar_cache (gvar_cache); + + return ret; } #endif @@ -548,12 +961,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tplan->new_to_old_gid_list) - | hb_map ([c, &_mtx, mtx_map] (hb_codepoint_pair_t _) + | hb_map ([&_mtx, mtx_map] (hb_codepoint_pair_t _) { hb_codepoint_t new_gid = _.first; hb_codepoint_t old_gid = _.second; @@ -246,8 +236,7 @@ struct hmtxvmtx if (!mtx_map->has (new_gid, &v)) { int lsb = 0; - if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb)) - (void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb); + _mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb); return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb); } return *v; @@ -326,49 +315,23 @@ struct hmtxvmtx bool has_data () const { return (bool) num_bearings; } - bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, + void get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, int *lsb) const { if (glyph < num_long_metrics) { *lsb = table->longMetricZ[glyph].sb; - return true; + return; } if (unlikely (glyph >= num_bearings)) - return false; + { + *lsb = 0; + return; + } const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; *lsb = bearings[glyph - num_long_metrics]; - return true; - } - - bool get_leading_bearing_with_var_unscaled (hb_font_t *font, - hb_codepoint_t glyph, - int *lsb) const - { - if (!font->num_coords) - return get_leading_bearing_without_var_unscaled (glyph, lsb); - -#ifndef HB_NO_VAR - float delta; - if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) && - get_leading_bearing_without_var_unscaled (glyph, lsb)) - { - *lsb += roundf (delta); - return true; - } - - // If there's no vmtx data, the phantom points from glyf table are not accurate, - // so we cannot take the next path. - bool is_vertical = T::tableTag == HB_OT_TAG_vmtx; - if (is_vertical && !has_data ()) - return false; - - return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb); -#else - return false; -#endif } unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const @@ -402,27 +365,17 @@ struct hmtxvmtx return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)]; } - unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, - hb_font_t *font, - ItemVariationStore::cache_t *store_cache = nullptr) const +#ifndef HB_NO_VAR + unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, + hb_font_t *font, + hb_scalar_cache_t *store_cache = nullptr) const { unsigned int advance = get_advance_without_var_unscaled (glyph); - -#ifndef HB_NO_VAR - if (unlikely (glyph >= num_bearings) || !font->num_coords) - return advance; - - if (var_table.get_length ()) - return advance + roundf (var_table->get_advance_delta_unscaled (glyph, - font->coords, font->num_coords, - store_cache)); - - unsigned glyf_advance = _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); - return glyf_advance ? glyf_advance : advance; -#else - return advance; -#endif + return hb_max(0.0f, advance + roundf (var_table->get_advance_delta_unscaled (glyph, + font->coords, font->num_coords, + store_cache))); } +#endif protected: // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh index 97d012e58b8..42326adcad2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh @@ -90,11 +90,11 @@ struct KernSubTableFormat3 template void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const { - set_t set; if (likely (glyphCount)) - set.add_range (0, glyphCount - 1); - left_set.union_ (set); - right_set.union_ (set); + { + left_set.add_range (0, num_glyphs - 1); + right_set.add_range (0, num_glyphs - 1); + } } protected: @@ -306,8 +306,8 @@ struct kern { static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; - bool has_data () const { return u.version32; } - unsigned get_type () const { return u.major; } + bool has_data () const { return u.version32.v; } + unsigned get_type () const { return u.major.v; } bool has_state_machine () const { @@ -363,7 +363,7 @@ struct kern bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.version32.sanitize (c)) return_trace (false); + if (!u.version32.v.sanitize (c)) return_trace (false); hb_barrier (); return_trace (dispatch (c)); } @@ -406,15 +406,15 @@ struct kern protected: union { - HBUINT32 version32; - HBUINT16 major; + struct { HBUINT32 v; } version32; + struct { HBUINT16 v; } major; KernOT ot; #ifndef HB_NO_AAT_SHAPE KernAAT aat; #endif } u; public: - DEFINE_SIZE_UNION (4, version32); + DEFINE_SIZE_UNION (4, version32.v); }; struct kern_accelerator_t : kern::accelerator_t { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh index 04a79a93a89..b9baaca2a23 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh @@ -165,13 +165,13 @@ struct BaseCoordFormat3 struct BaseCoord { - bool has_data () const { return u.format; } + bool has_data () const { return u.format.v; } hb_position_t get_coord (hb_font_t *font, const ItemVariationStore &var_store, hb_direction_t direction) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_coord (font, direction); case 2: hb_barrier (); return u.format2.get_coord (font, direction); case 3: hb_barrier (); return u.format3.get_coord (font, var_store, direction); @@ -181,7 +181,7 @@ struct BaseCoord void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 3: hb_barrier (); u.format3.collect_variation_indices (varidx_set); return; default:return; } @@ -190,9 +190,9 @@ struct BaseCoord template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -203,9 +203,9 @@ struct BaseCoord bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (!u.format.sanitize (c))) return_trace (false); + if (unlikely (!u.format.v.sanitize (c))) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); @@ -215,13 +215,13 @@ struct BaseCoord protected: union { - HBUINT16 format; + struct { HBUINT16 v; } format; BaseCoordFormat1 format1; BaseCoordFormat2 format2; BaseCoordFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct FeatMinMaxRecord diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh index 7ee34185575..dcacc9cb86c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh @@ -141,6 +141,7 @@ struct hb_subset_layout_context_t : const hb_map_t *lookup_index_map; const hb_hashmap_t> *script_langsys_map; const hb_map_t *feature_index_map; + const hb_map_t *feature_map_w_duplicates; const hb_hashmap_t *feature_substitutes_map; hb_hashmap_t> *feature_record_cond_idx_map; const hb_set_t *catch_all_record_feature_idxes; @@ -165,6 +166,7 @@ struct hb_subset_layout_context_t : lookup_index_map = &c_->plan->gsub_lookups; script_langsys_map = &c_->plan->gsub_langsys; feature_index_map = &c_->plan->gsub_features; + feature_map_w_duplicates = &c_->plan->gsub_features_w_duplicates; feature_substitutes_map = &c_->plan->gsub_feature_substitutes_map; feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gsub_feature_record_cond_idx_map; catch_all_record_feature_idxes = &c_->plan->gsub_old_features; @@ -175,6 +177,7 @@ struct hb_subset_layout_context_t : lookup_index_map = &c_->plan->gpos_lookups; script_langsys_map = &c_->plan->gpos_langsys; feature_index_map = &c_->plan->gpos_features; + feature_map_w_duplicates = &c_->plan->gpos_features_w_duplicates; feature_substitutes_map = &c_->plan->gpos_feature_substitutes_map; feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gpos_feature_record_cond_idx_map; catch_all_record_feature_idxes = &c_->plan->gpos_old_features; @@ -825,46 +828,9 @@ struct Feature const Record_sanitize_closure_t *closure = nullptr) const { TRACE_SANITIZE (this); - if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) - return_trace (false); - hb_barrier (); - - /* Some earlier versions of Adobe tools calculated the offset of the - * FeatureParams subtable from the beginning of the FeatureList table! - * - * If sanitizing "failed" for the FeatureParams subtable, try it with the - * alternative location. We would know sanitize "failed" if old value - * of the offset was non-zero, but it's zeroed now. - * - * Only do this for the 'size' feature, since at the time of the faulty - * Adobe tools, only the 'size' feature had FeatureParams defined. - */ - - if (likely (featureParams.is_null ())) - return_trace (true); - - unsigned int orig_offset = featureParams; - if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) - return_trace (false); - hb_barrier (); - - if (featureParams == 0 && closure && - closure->tag == HB_TAG ('s','i','z','e') && - closure->list_base && closure->list_base < this) - { - unsigned int new_offset_int = orig_offset - - (((char *) this) - ((char *) closure->list_base)); - - Offset16To new_offset; - /* Check that it would not overflow. */ - new_offset = new_offset_int; - if (new_offset == new_offset_int && - c->try_set (&featureParams, new_offset_int) && - !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) - return_trace (false); - } - - return_trace (true); + return_trace (c->check_struct (this) && + featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE) && + lookupIndex.sanitize (c)); } Offset16To @@ -1082,15 +1048,15 @@ struct LangSys if (unlikely (!c->serializer->extend_min (out))) return_trace (false); const uint32_t *v; - out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu; + out->reqFeatureIndex = l->feature_map_w_duplicates->has (reqFeatureIndex, &v) ? *v : 0xFFFFu; if (!l->visitFeatureIndex (featureIndex.len)) return_trace (false); auto it = + hb_iter (featureIndex) - | hb_filter (l->feature_index_map) - | hb_map (l->feature_index_map) + | hb_filter (l->feature_map_w_duplicates) + | hb_map (l->feature_map_w_duplicates) ; bool ret = bool (it); @@ -1337,7 +1303,7 @@ struct Lookup TRACE_DISPATCH (this, lookup_type); unsigned int count = get_subtable_count (); for (unsigned int i = 0; i < count; i++) { - typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type, std::forward (ds)...); + typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type, ds...); if (c->stop_sublookup_iteration (r)) return_trace (r); } @@ -1387,6 +1353,11 @@ struct Lookup { unsigned new_flag = lookupFlag; new_flag &= ~LookupFlag::UseMarkFilteringSet; + // https://github.com/harfbuzz/harfbuzz/issues/5499 + // If we remove UseMarkFilteringSet flag because the set is now empty, + // we need to add IgnoreMarks flag, otherwise the lookup will not + // ignore any marks, which changes the behavior. + new_flag |= LookupFlag::IgnoreMarks; out->lookupFlag = new_flag; } else @@ -1425,7 +1396,7 @@ struct Lookup if (unlikely (!get_subtables ().sanitize (c, this, get_type ()))) return_trace (false); - if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) + if (unlikely (get_type () == TSubTable::Extension)) { hb_barrier (); @@ -1433,11 +1404,6 @@ struct Lookup * have the same type, which shall not be the Extension type * itself (but we already checked for that). * This is specially important if one has a reverse type! - * - * We only do this if sanitizer edit_count is zero. Otherwise, - * some of the subtables might have become insane after they - * were sanity-checked by the edits of subsequent subtables. - * https://bugs.chromium.org/p/chromium/issues/detail?id=960331 */ unsigned int type = get_subtable (0).u.extension.get_type (); for (unsigned int i = 1; i < subtables; i++) @@ -2067,7 +2033,7 @@ struct ClassDef unsigned int get (hb_codepoint_t k) const { return get_class (k); } unsigned int get_class (hb_codepoint_t glyph_id) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_class (glyph_id); case 2: hb_barrier (); return u.format2.get_class (glyph_id); #ifndef HB_NO_BEYOND_64K @@ -2078,7 +2044,7 @@ struct ClassDef } } unsigned int get_class (hb_codepoint_t glyph_id, - hb_ot_lookup_cache_t *cache) const + hb_ot_layout_mapping_cache_t *cache) const { unsigned klass; if (cache && cache->get (glyph_id, &klass)) return klass; @@ -2089,7 +2055,7 @@ struct ClassDef unsigned get_population () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_population (); case 2: hb_barrier (); return u.format2.get_population (); #ifndef HB_NO_BEYOND_64K @@ -2142,7 +2108,7 @@ struct ClassDef #ifndef HB_NO_BEYOND_64K if (glyph_max > 0xFFFFu) - u.format += 2; + u.format.v += 2; if (unlikely (glyph_max > 0xFFFFFFu)) #else if (unlikely (glyph_max > 0xFFFFu)) @@ -2152,9 +2118,9 @@ struct ClassDef return_trace (false); } - u.format = format; + u.format.v = format; - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.serialize (c, it)); case 2: hb_barrier (); return_trace (u.format2.serialize (c, it)); @@ -2173,7 +2139,7 @@ struct ClassDef const Coverage* glyph_filter = nullptr) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); case 2: hb_barrier (); return_trace (u.format2.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); #ifndef HB_NO_BEYOND_64K @@ -2187,9 +2153,9 @@ struct ClassDef bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); #ifndef HB_NO_BEYOND_64K @@ -2202,7 +2168,7 @@ struct ClassDef unsigned cost () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.cost (); case 2: hb_barrier (); return u.format2.cost (); #ifndef HB_NO_BEYOND_64K @@ -2218,7 +2184,7 @@ struct ClassDef template bool collect_coverage (set_t *glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.collect_coverage (glyphs); case 2: hb_barrier (); return u.format2.collect_coverage (glyphs); #ifndef HB_NO_BEYOND_64K @@ -2234,7 +2200,7 @@ struct ClassDef template bool collect_class (set_t *glyphs, unsigned int klass) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.collect_class (glyphs, klass); case 2: hb_barrier (); return u.format2.collect_class (glyphs, klass); #ifndef HB_NO_BEYOND_64K @@ -2247,7 +2213,7 @@ struct ClassDef bool intersects (const hb_set_t *glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersects (glyphs); case 2: hb_barrier (); return u.format2.intersects (glyphs); #ifndef HB_NO_BEYOND_64K @@ -2259,7 +2225,7 @@ struct ClassDef } bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersects_class (glyphs, klass); case 2: hb_barrier (); return u.format2.intersects_class (glyphs, klass); #ifndef HB_NO_BEYOND_64K @@ -2272,7 +2238,7 @@ struct ClassDef void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersected_class_glyphs (glyphs, klass, intersect_glyphs); case 2: hb_barrier (); return u.format2.intersected_class_glyphs (glyphs, klass, intersect_glyphs); #ifndef HB_NO_BEYOND_64K @@ -2285,7 +2251,7 @@ struct ClassDef void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersected_classes (glyphs, intersect_classes); case 2: hb_barrier (); return u.format2.intersected_classes (glyphs, intersect_classes); #ifndef HB_NO_BEYOND_64K @@ -2299,7 +2265,7 @@ struct ClassDef protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ClassDefFormat1_3 format1; ClassDefFormat2_4 format2; #ifndef HB_NO_BEYOND_64K @@ -2308,7 +2274,7 @@ struct ClassDef #endif } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; template @@ -2326,151 +2292,177 @@ struct delta_row_encoding_t { /* each byte represents a region, value is one of 0/1/2/4, which means bytes * needed for this region */ - hb_vector_t chars; + struct chars_t : hb_vector_t + { + int cmp (const chars_t& other) const + { + return as_array ().cmp (other.as_array ()); + } + + hb_pair_t get_width () + { + unsigned width = 0; + unsigned columns = 0; + for (unsigned i = 0; i < length; i++) + { + unsigned v = arrayZ[i]; + width += v; + columns += (v != 0); + } + return hb_pair (width, columns); + } + + HB_HOT + hb_pair_t combine_width (const chars_t& other) const + { + unsigned combined_width = 0; + unsigned combined_columns = 0; + for (unsigned i = 0; i < length; i++) + { + unsigned v = hb_max (arrayZ[i], other.arrayZ[i]); + combined_width += v; + combined_columns += (v != 0); + } + return hb_pair (combined_width, combined_columns); + } + }; + + hb_pair_t combine_width (const delta_row_encoding_t& other_encoding) const { return chars.combine_width (other_encoding.chars); } + + // Actual data + + chars_t chars; unsigned width = 0; - hb_vector_t columns; unsigned overhead = 0; hb_vector_t*> items; delta_row_encoding_t () = default; - delta_row_encoding_t (hb_vector_t&& chars_, - const hb_vector_t* row = nullptr) : - delta_row_encoding_t () - + delta_row_encoding_t (hb_vector_t*> &&rows, unsigned num_cols) { - chars = std::move (chars_); - width = get_width (); - columns = get_columns (); - overhead = get_chars_overhead (columns); - if (row) items.push (row); + assert (rows); + + items = std::move (rows); + + if (unlikely (!chars.resize (num_cols))) + return; + + calculate_chars (); + } + + void merge (const delta_row_encoding_t& other) + { + items.alloc (items.length + other.items.length); + for (auto &row : other.items) + add_row (row); + + // Merge chars + assert (chars.length == other.chars.length); + for (unsigned i = 0; i < chars.length; i++) + chars.arrayZ[i] = hb_max (chars.arrayZ[i], other.chars.arrayZ[i]); + chars_changed (); + } + + void chars_changed () + { + auto _ = chars.get_width (); + width = _.first; + overhead = get_chars_overhead (_.second); + } + + void calculate_chars () + { + assert (items); + + bool long_words = false; + + for (auto &row : items) + { + assert (row->length == chars.length); + + /* 0/1/2 byte encoding */ + for (unsigned i = 0; i < row->length; i++) + { + int v = row->arrayZ[i]; + if (v == 0) + continue; + else if (v > 32767 || v < -32768) + { + long_words = true; + chars.arrayZ[i] = hb_max (chars.arrayZ[i], 4); + } + else if (v > 127 || v < -128) + chars.arrayZ[i] = hb_max (chars.arrayZ[i], 2); + else + chars.arrayZ[i] = hb_max (chars.arrayZ[i], 1); + } + } + + if (long_words) + { + // Convert 1s to 2s + for (auto &v : chars) + if (v == 1) + v = 2; + } + + chars_changed (); } bool is_empty () const { return !items; } - static hb_vector_t get_row_chars (const hb_vector_t& row) - { - hb_vector_t ret; - if (!ret.alloc (row.length)) return ret; - - bool long_words = false; - - /* 0/1/2 byte encoding */ - for (int i = row.length - 1; i >= 0; i--) - { - int v = row.arrayZ[i]; - if (v == 0) - ret.push (0); - else if (v > 32767 || v < -32768) - { - long_words = true; - break; - } - else if (v > 127 || v < -128) - ret.push (2); - else - ret.push (1); - } - - if (!long_words) - return ret; - - /* redo, 0/2/4 bytes encoding */ - ret.reset (); - for (int i = row.length - 1; i >= 0; i--) - { - int v = row.arrayZ[i]; - if (v == 0) - ret.push (0); - else if (v > 32767 || v < -32768) - ret.push (4); - else - ret.push (2); - } - return ret; - } - - inline unsigned get_width () - { - unsigned ret = + hb_iter (chars) - | hb_reduce (hb_add, 0u) - ; - return ret; - } - - hb_vector_t get_columns () - { - hb_vector_t cols; - cols.alloc (chars.length); - for (auto v : chars) - { - uint8_t flag = v ? 1 : 0; - cols.push (flag); - } - return cols; - } - - static inline unsigned get_chars_overhead (const hb_vector_t& cols) + static inline unsigned get_chars_overhead (unsigned num_columns) { unsigned c = 4 + 6; // 4 bytes for LOffset, 6 bytes for VarData header - unsigned cols_bit_count = 0; - for (auto v : cols) - if (v) cols_bit_count++; - return c + cols_bit_count * 2; + return c + num_columns * 2; } - unsigned get_gain () const + unsigned get_gain (unsigned additional_bytes_per_rows = 1) const { int count = items.length; - return hb_max (0, (int) overhead - count); + return hb_max (0, (int) overhead - count * (int) additional_bytes_per_rows); } int gain_from_merging (const delta_row_encoding_t& other_encoding) const { - int combined_width = 0; - for (unsigned i = 0; i < chars.length; i++) - combined_width += hb_max (chars.arrayZ[i], other_encoding.chars.arrayZ[i]); + // Back of the envelope calculations to reject early. + signed additional_bytes_per_rows = other_encoding.width - width; + if (additional_bytes_per_rows > 0) + { + if (get_gain (additional_bytes_per_rows) == 0) + return 0; + } + else + { + if (other_encoding.get_gain (-additional_bytes_per_rows) == 0) + return 0; + } - hb_vector_t combined_columns; - combined_columns.alloc (columns.length); - for (unsigned i = 0; i < columns.length; i++) - combined_columns.push (columns.arrayZ[i] | other_encoding.columns.arrayZ[i]); + auto pair = combine_width (other_encoding); + unsigned combined_width = pair.first; + unsigned combined_columns = pair.second; - int combined_overhead = get_chars_overhead (combined_columns); - int combined_gain = (int) overhead + (int) other_encoding.overhead - combined_overhead - - (combined_width - (int) width) * items.length - - (combined_width - (int) other_encoding.width) * other_encoding.items.length; + int combined_gain = (int) overhead + (int) other_encoding.overhead; + combined_gain -= (combined_width - (int) width) * items.length; + combined_gain -= (combined_width - (int) other_encoding.width) * other_encoding.items.length; + combined_gain -= get_chars_overhead (combined_columns); return combined_gain; } + bool add_row (const hb_vector_t* row) + { return items.push (row); } + static int cmp (const void *pa, const void *pb) { const delta_row_encoding_t *a = (const delta_row_encoding_t *)pa; const delta_row_encoding_t *b = (const delta_row_encoding_t *)pb; - int gain_a = a->get_gain (); - int gain_b = b->get_gain (); - - if (gain_a != gain_b) - return gain_a - gain_b; - - return (b->chars).as_array ().cmp ((a->chars).as_array ()); - } - - static int cmp_width (const void *pa, const void *pb) - { - const delta_row_encoding_t *a = (const delta_row_encoding_t *)pa; - const delta_row_encoding_t *b = (const delta_row_encoding_t *)pb; - if (a->width != b->width) return (int) a->width - (int) b->width; - return (b->chars).as_array ().cmp ((a->chars).as_array ()); + return b->chars.cmp (a->chars); } - - bool add_row (const hb_vector_t* row) - { return items.push (row); } }; struct VarRegionAxis @@ -2548,32 +2540,112 @@ struct SparseVarRegionAxis DEFINE_SIZE_STATIC (8); }; -#define REGION_CACHE_ITEM_CACHE_INVALID INT_MIN -#define REGION_CACHE_ITEM_MULTIPLIER (float (1 << ((sizeof (int) * 8) - 2))) -#define REGION_CACHE_ITEM_DIVISOR (1.f / float (1 << ((sizeof (int) * 8) - 2))) +struct hb_scalar_cache_t +{ + private: + static constexpr unsigned STATIC_LENGTH = 16; + static constexpr int INVALID = INT_MIN; + static constexpr float MULTIPLIER = 1 << ((sizeof (int) * 8) - 2); + static constexpr float DIVISOR = 1.f / MULTIPLIER; + + public: + hb_scalar_cache_t () : length (STATIC_LENGTH) { clear (); } + + hb_scalar_cache_t (const hb_scalar_cache_t&) = delete; + hb_scalar_cache_t (hb_scalar_cache_t&&) = delete; + hb_scalar_cache_t& operator= (const hb_scalar_cache_t&) = delete; + hb_scalar_cache_t& operator= (hb_scalar_cache_t&&) = delete; + + static hb_scalar_cache_t *create (unsigned int count, + hb_scalar_cache_t *scratch_cache = nullptr) + { + if (!count) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t); + + if (scratch_cache && count <= scratch_cache->length) + { + scratch_cache->clear (); + return scratch_cache; + } + + auto *cache = (hb_scalar_cache_t *) hb_malloc (sizeof (hb_scalar_cache_t) - sizeof (static_values) + sizeof (static_values[0]) * count); + if (unlikely (!cache)) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t); + + cache->length = count; + cache->clear (); + + return cache; + } + + static void destroy (hb_scalar_cache_t *cache, + hb_scalar_cache_t *scratch_cache = nullptr) + { + if (cache != &Null(hb_scalar_cache_t) && cache != scratch_cache) + hb_free (cache); + } + + void clear () + { + auto *values = &static_values[0]; + unsigned i = 0; +#ifndef HB_OPTIMIZE_SIZE + for (; i + 3 < length; i += 4) + { + values[i + 0] = INVALID; + values[i + 1] = INVALID; + values[i + 2] = INVALID; + values[i + 3] = INVALID; + } +#endif + for (; i < length; i++) + values[i] = INVALID; + } + + HB_ALWAYS_INLINE + bool get (unsigned i, float *value) const + { + if (unlikely (i >= length)) + { + *value = 0.f; + return true; + } + auto *values = &static_values[0]; + auto *cached_value = &values[i]; + // Super hot. Most common path is that we have a cached value of 0. + int v = *cached_value; + if (likely (!v)) + { + *value = 0.f; + return true; + } + if (v == INVALID) + return false; + *value = v * DIVISOR; + return true; + } + + HB_ALWAYS_INLINE + void set (unsigned i, float value) + { + if (unlikely (i >= length)) return; + auto *values = &static_values[0]; + auto *cached_value = &values[i]; + *cached_value = roundf(value * MULTIPLIER); + } + + private: + unsigned length; + mutable hb_atomic_t static_values[STATIC_LENGTH]; +}; struct VarRegionList { - using cache_t = hb_atomic_t; - - float evaluate (unsigned int region_index, - const int *coords, unsigned int coord_len, - cache_t *cache = nullptr) const + private: + float evaluate_impl (unsigned int region_index, + const int *coords, unsigned int coord_len) const { - if (unlikely (region_index >= regionCount)) - return 0.; - - cache_t *cached_value = nullptr; - if (cache) - { - cached_value = &(cache[region_index]); - if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID) - return *cached_value * REGION_CACHE_ITEM_DIVISOR; - } - const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount); + float v = 1.f; - float v = 1.; unsigned int count = axisCount; for (unsigned int i = 0; i < count; i++) { @@ -2581,15 +2653,32 @@ struct VarRegionList float factor = axes[i].evaluate (coord); if (factor == 0.f) { - if (cache) - *cached_value = 0.; - return 0.; + v = 0.f; + break; } v *= factor; } + return v; + } + + public: + HB_ALWAYS_INLINE + float evaluate (unsigned int region_index, + const int *coords, unsigned int coord_len, + hb_scalar_cache_t *cache = nullptr) const + { + if (unlikely (region_index >= regionCount)) + return 0.; + + float v; + if (cache && cache->get (region_index, &v)) + return v; + + v = evaluate_impl (region_index, coords, coord_len); + if (cache) - *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER; + cache->set (region_index, v); return v; } @@ -2732,29 +2821,24 @@ struct SparseVariationRegion : Array16Of struct SparseVarRegionList { - using cache_t = hb_atomic_t; - + HB_ALWAYS_INLINE float evaluate (unsigned int region_index, const int *coords, unsigned int coord_len, - cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { if (unlikely (region_index >= regions.len)) return 0.; - cache_t *cached_value = nullptr; - if (cache) - { - cached_value = &(cache[region_index]); - if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID) - return *cached_value * REGION_CACHE_ITEM_DIVISOR; - } + float v; + if (cache && cache->get (region_index, &v)) + return v; const SparseVariationRegion ®ion = this+regions[region_index]; - float v = region.evaluate (coords, coord_len); - + v = region.evaluate (coords, coord_len); if (cache) - *cached_value = v * REGION_CACHE_ITEM_MULTIPLIER; + cache->set (region_index, v); + return v; } @@ -2792,46 +2876,62 @@ struct VarData + itemCount * get_row_size (); } - float get_delta (unsigned int inner, - const int *coords, unsigned int coord_count, - const VarRegionList ®ions, - VarRegionList::cache_t *cache = nullptr) const + float _get_delta (unsigned int inner, + const int *coords, unsigned int coord_count, + const VarRegionList ®ions, + hb_scalar_cache_t *cache = nullptr) const { if (unlikely (inner >= itemCount)) return 0.; + bool is_long = longWords (); + unsigned int count = regionIndices.len; + unsigned word_count = wordCount (); + unsigned int scount = is_long ? count : word_count; + unsigned int lcount = is_long ? word_count : 0; - unsigned int count = regionIndices.len; - bool is_long = longWords (); - unsigned word_count = wordCount (); - unsigned int scount = is_long ? count : word_count; - unsigned int lcount = is_long ? word_count : 0; + const HBUINT8 *bytes = get_delta_bytes (); + const HBUINT8 *row = bytes + inner * get_row_size (); - const HBUINT8 *bytes = get_delta_bytes (); - const HBUINT8 *row = bytes + inner * get_row_size (); + float delta = 0.; + unsigned int i = 0; - float delta = 0.; - unsigned int i = 0; + const HBINT32 *lcursor = reinterpret_cast (row); + for (; i < lcount; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + if (scalar) + delta += scalar * *lcursor; + lcursor++; + } + const HBINT16 *scursor = reinterpret_cast (lcursor); + for (; i < scount; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + if (scalar) + delta += scalar * *scursor; + scursor++; + } + const HBINT8 *bcursor = reinterpret_cast (scursor); + for (; i < count; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + if (scalar) + delta += scalar * *bcursor; + bcursor++; + } - const HBINT32 *lcursor = reinterpret_cast (row); - for (; i < lcount; i++) - { - float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); - delta += scalar * *lcursor++; - } - const HBINT16 *scursor = reinterpret_cast (lcursor); - for (; i < scount; i++) - { - float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); - delta += scalar * *scursor++; - } - const HBINT8 *bcursor = reinterpret_cast (scursor); - for (; i < count; i++) - { - float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); - delta += scalar * *bcursor++; - } + return delta; + } - return delta; + HB_ALWAYS_INLINE + float get_delta (unsigned int inner, + const int *coords, unsigned int coord_count, + const VarRegionList ®ions, + hb_scalar_cache_t *cache = nullptr) const + { + unsigned int count = regionIndices.len; + if (!count) return 0.f; // This is quite common, so optimize it. + return _get_delta (inner, coords, coord_count, regions, cache); } void get_region_scalars (const int *coords, unsigned int coord_count, @@ -3150,7 +3250,7 @@ struct MultiVarData const int *coords, unsigned int coord_count, const SparseVarRegionList ®ions, hb_array_t out, - SparseVarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { auto &deltaSets = StructAfter (regionIndices); @@ -3187,31 +3287,24 @@ struct MultiVarData struct ItemVariationStore { friend struct item_variations_t; - using cache_t = VarRegionList::cache_t; - cache_t *create_cache () const + hb_scalar_cache_t *create_cache () const { #ifdef HB_NO_VAR - return nullptr; + return hb_scalar_cache_t::create (0); #endif - unsigned count = (this+regions).regionCount; - if (!count) return nullptr; - - cache_t *cache = (cache_t *) hb_malloc (sizeof (float) * count); - if (unlikely (!cache)) return nullptr; - - for (unsigned i = 0; i < count; i++) - cache[i] = REGION_CACHE_ITEM_CACHE_INVALID; - - return cache; + return hb_scalar_cache_t::create ((this+regions).regionCount); } - static void destroy_cache (cache_t *cache) { hb_free (cache); } + static void destroy_cache (hb_scalar_cache_t *cache) + { + hb_scalar_cache_t::destroy (cache); + } private: float get_delta (unsigned int outer, unsigned int inner, const int *coords, unsigned int coord_count, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { #ifdef HB_NO_VAR return 0.f; @@ -3229,7 +3322,7 @@ struct ItemVariationStore public: float get_delta (unsigned int index, const int *coords, unsigned int coord_count, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { unsigned int outer = index >> 16; unsigned int inner = index & 0xFFFF; @@ -3237,7 +3330,7 @@ struct ItemVariationStore } float get_delta (unsigned int index, hb_array_t coords, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { return get_delta (index, coords.arrayZ, coords.length, @@ -3356,7 +3449,7 @@ struct ItemVariationStore for (unsigned i = 0; i < count; i++) { hb_inc_bimap_t *map = inner_maps.push (); - if (!c->propagate_error(inner_maps)) + if (unlikely (!c->propagate_error(inner_maps))) return_trace(nullptr); auto &data = this+dataSets[i]; @@ -3445,43 +3538,28 @@ struct ItemVariationStore struct MultiItemVariationStore { - using cache_t = SparseVarRegionList::cache_t; - - cache_t *create_cache (hb_array_t static_cache = hb_array_t ()) const + hb_scalar_cache_t *create_cache (hb_scalar_cache_t *static_cache = nullptr) const { #ifdef HB_NO_VAR - return nullptr; + return hb_scalar_cache_t::create (0); #endif auto &r = this+regions; unsigned count = r.regions.len; - cache_t *cache; - if (count <= static_cache.length) - cache = static_cache.arrayZ; - else - { - cache = (cache_t *) hb_malloc (sizeof (float) * count); - if (unlikely (!cache)) return nullptr; - } - - for (unsigned i = 0; i < count; i++) - cache[i] = REGION_CACHE_ITEM_CACHE_INVALID; - - return cache; + return hb_scalar_cache_t::create (count, static_cache); } - static void destroy_cache (cache_t *cache, - hb_array_t static_cache = hb_array_t ()) + static void destroy_cache (hb_scalar_cache_t *cache, + hb_scalar_cache_t *static_cache = nullptr) { - if (cache != static_cache.arrayZ) - hb_free (cache); + hb_scalar_cache_t::destroy (cache, static_cache); } private: void get_delta (unsigned int outer, unsigned int inner, const int *coords, unsigned int coord_count, hb_array_t out, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { #ifdef HB_NO_VAR return; @@ -3501,7 +3579,7 @@ struct MultiItemVariationStore void get_delta (unsigned int index, const int *coords, unsigned int coord_count, hb_array_t out, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { unsigned int outer = index >> 16; unsigned int inner = index & 0xFFFF; @@ -3510,7 +3588,7 @@ struct MultiItemVariationStore void get_delta (unsigned int index, hb_array_t coords, hb_array_t out, - VarRegionList::cache_t *cache = nullptr) const + hb_scalar_cache_t *cache = nullptr) const { return get_delta (index, coords.arrayZ, coords.length, @@ -3540,8 +3618,6 @@ struct MultiItemVariationStore DEFINE_SIZE_ARRAY_SIZED (8, dataSets); }; -#undef REGION_CACHE_ITEM_CACHE_INVALID - template struct DeltaSetIndexMapFormat01 { @@ -3592,13 +3668,19 @@ struct DeltaSetIndexMapFormat01 return_trace (true); } + HB_ALWAYS_INLINE uint32_t map (unsigned int v) const /* Returns 16.16 outer.inner. */ { /* If count is zero, pass value unchanged. This takes * care of direct mapping for advance map. */ if (!mapCount) return v; + return _map (v); + } + HB_HOT + uint32_t _map (unsigned int v) const /* Returns 16.16 outer.inner. */ + { if (v >= mapCount) v = mapCount - 1; @@ -3654,8 +3736,8 @@ struct DeltaSetIndexMap { TRACE_SERIALIZE (this); unsigned length = plan.get_output_map ().length; - u.format = length <= 0xFFFF ? 0 : 1; - switch (u.format) { + u.format.v = length <= 0xFFFF ? 0 : 1; + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.serialize (c, plan)); case 1: hb_barrier (); return_trace (u.format1.serialize (c, plan)); default:return_trace (false); @@ -3664,7 +3746,7 @@ struct DeltaSetIndexMap uint32_t map (unsigned v) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return (u.format0.map (v)); case 1: hb_barrier (); return (u.format1.map (v)); default:return v; @@ -3673,7 +3755,7 @@ struct DeltaSetIndexMap unsigned get_map_count () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_map_count (); case 1: hb_barrier (); return u.format1.get_map_count (); default:return 0; @@ -3682,7 +3764,7 @@ struct DeltaSetIndexMap unsigned get_width () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_width (); case 1: hb_barrier (); return u.format1.get_width (); default:return 0; @@ -3691,7 +3773,7 @@ struct DeltaSetIndexMap unsigned get_inner_bit_count () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_inner_bit_count (); case 1: hb_barrier (); return u.format1.get_inner_bit_count (); default:return 0; @@ -3701,9 +3783,9 @@ struct DeltaSetIndexMap bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c)); case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); default:return_trace (true); @@ -3713,7 +3795,7 @@ struct DeltaSetIndexMap DeltaSetIndexMap* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (reinterpret_cast (u.format0.copy (c))); case 1: hb_barrier (); return_trace (reinterpret_cast (u.format1.copy (c))); default:return_trace (nullptr); @@ -3722,12 +3804,12 @@ struct DeltaSetIndexMap protected: union { - HBUINT8 format; /* Format identifier */ + struct { HBUINT8 v; } format; /* Format identifier */ DeltaSetIndexMapFormat01 format0; DeltaSetIndexMapFormat01 format1; } u; public: - DEFINE_SIZE_UNION (1, format); + DEFINE_SIZE_UNION (1, format.v); }; @@ -3736,7 +3818,7 @@ struct ItemVarStoreInstancer ItemVarStoreInstancer (const ItemVariationStore *varStore_, const DeltaSetIndexMap *varIdxMap, hb_array_t coords, - VarRegionList::cache_t *cache = nullptr) : + hb_scalar_cache_t *cache = nullptr) : varStore (varStore_), varIdxMap (varIdxMap), coords (coords), cache (cache) { if (!varStore) @@ -3762,7 +3844,7 @@ struct ItemVarStoreInstancer const ItemVariationStore *varStore; const DeltaSetIndexMap *varIdxMap; hb_array_t coords; - VarRegionList::cache_t *cache; + hb_scalar_cache_t *cache; }; struct MultiItemVarStoreInstancer @@ -3770,7 +3852,7 @@ struct MultiItemVarStoreInstancer MultiItemVarStoreInstancer (const MultiItemVariationStore *varStore, const DeltaSetIndexMap *varIdxMap, hb_array_t coords, - SparseVarRegionList::cache_t *cache = nullptr) : + hb_scalar_cache_t *cache = nullptr) : varStore (varStore), varIdxMap (varIdxMap), coords (coords), cache (cache) { if (!varStore) @@ -3803,7 +3885,7 @@ struct MultiItemVarStoreInstancer const MultiItemVariationStore *varStore; const DeltaSetIndexMap *varIdxMap; hb_array_t coords; - SparseVarRegionList::cache_t *cache; + hb_scalar_cache_t *cache; }; @@ -4130,7 +4212,7 @@ struct Condition bool evaluate (const int *coords, unsigned int coord_len, Instancer *instancer) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.evaluate (coords, coord_len, instancer); case 2: hb_barrier (); return u.format2.evaluate (coords, coord_len, instancer); case 3: hb_barrier (); return u.format3.evaluate (coords, coord_len, instancer); @@ -4143,7 +4225,7 @@ struct Condition Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, hb_map_t *condition_map /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.keep_with_variations (c, condition_map); // TODO(subset) default: c->apply = false; return KEEP_COND_WITH_VAR; @@ -4153,9 +4235,9 @@ struct Condition template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -4168,9 +4250,9 @@ struct Condition bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); @@ -4182,7 +4264,7 @@ struct Condition protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ConditionAxisRange format1; ConditionValue format2; ConditionAnd format3; @@ -4190,7 +4272,7 @@ struct Condition ConditionNegate format5; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; template @@ -4353,7 +4435,7 @@ struct FeatureTableSubstitutionRecord if (unlikely (!s->extend_min (this))) return_trace (false); uint32_t *new_feature_idx; - if (!c->feature_index_map->has (feature_index, &new_feature_idx)) + if (!c->feature_map_w_duplicates->has (feature_index, &new_feature_idx)) return_trace (false); if (!s->check_assign (featureIndex, *new_feature_idx, HB_SERIALIZE_ERROR_INT_OVERFLOW)) @@ -4371,7 +4453,7 @@ struct FeatureTableSubstitutionRecord { TRACE_SUBSET (this); uint32_t *new_feature_index; - if (!c->feature_index_map->has (featureIndex, &new_feature_index)) + if (!c->feature_map_w_duplicates->has (featureIndex, &new_feature_index)) return_trace (false); auto *out = c->subset_context->serializer->embed (this); @@ -4645,7 +4727,7 @@ struct FeatureVariations int keep_up_to = -1; for (int i = varRecords.len - 1; i >= 0; i--) { - if (varRecords[i].intersects_features (this, l->feature_index_map)) { + if (varRecords[i].intersects_features (this, l->feature_map_w_duplicates)) { keep_up_to = i; break; } @@ -4783,13 +4865,13 @@ struct VariationDevice hb_position_t get_x_delta (hb_font_t *font, const ItemVariationStore &store, - ItemVariationStore::cache_t *store_cache = nullptr) const - { return !font->num_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); } + hb_scalar_cache_t *store_cache = nullptr) const + { return !font->has_nonzero_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); } hb_position_t get_y_delta (hb_font_t *font, const ItemVariationStore &store, - ItemVariationStore::cache_t *store_cache = nullptr) const - { return !font->num_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); } + hb_scalar_cache_t *store_cache = nullptr) const + { return !font->has_nonzero_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); } VariationDevice* copy (hb_serialize_context_t *c, const hb_hashmap_t> *layout_variation_idx_delta_map) const @@ -4823,9 +4905,9 @@ struct VariationDevice float get_delta (hb_font_t *font, const ItemVariationStore &store, - ItemVariationStore::cache_t *store_cache = nullptr) const + hb_scalar_cache_t *store_cache = nullptr) const { - return store.get_delta (varIdx, font->coords, font->num_coords, (ItemVariationStore::cache_t *) store_cache); + return store.get_delta (varIdx, font->coords, font->num_coords, store_cache); } protected: @@ -4850,7 +4932,7 @@ struct Device { hb_position_t get_x_delta (hb_font_t *font, const ItemVariationStore &store=Null (ItemVariationStore), - ItemVariationStore::cache_t *store_cache = nullptr) const + hb_scalar_cache_t *store_cache = nullptr) const { switch (u.b.format) { @@ -4868,7 +4950,7 @@ struct Device } hb_position_t get_y_delta (hb_font_t *font, const ItemVariationStore &store=Null (ItemVariationStore), - ItemVariationStore::cache_t *store_cache = nullptr) const + hb_scalar_cache_t *store_cache = nullptr) const { switch (u.b.format) { @@ -4954,6 +5036,18 @@ struct Device } } + bool is_variation_device () const + { + switch (u.b.format) { +#ifndef HB_NO_VAR + case 0x8000: + return true; +#endif + default: + return false; + } + } + protected: union { DeviceHeader b; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh index 0cfa139a260..f6aabc84ac9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh @@ -63,12 +63,20 @@ inline bool PosLookup::dispatch_recurse_func (hb_ot_apply c->set_lookup_index (lookup_index); c->set_lookup_props (l.get_props ()); + uint32_t stack_match_positions[8]; + hb_vector_t saved_match_positions; + saved_match_positions.set_storage (stack_match_positions); + hb_swap (c->match_positions, saved_match_positions); + bool ret = false; auto *accel = gpos->get_accel (lookup_index); - ret = accel && accel->apply (c, l.get_subtable_count (), false); + ret = accel && accel->apply (c, false); c->set_lookup_index (saved_lookup_index); c->set_lookup_props (saved_lookup_props); + + hb_swap (c->match_positions, saved_match_positions); + return ret; } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh index fd8a68be02d..8d5e1f18638 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh @@ -76,12 +76,20 @@ inline bool SubstLookup::dispatch_recurse_func (hb_ot_app c->set_lookup_index (lookup_index); c->set_lookup_props (l.get_props ()); + uint32_t stack_match_positions[8]; + hb_vector_t saved_match_positions; + saved_match_positions.set_storage (stack_match_positions); + hb_swap (c->match_positions, saved_match_positions); + bool ret = false; auto *accel = gsub->get_accel (lookup_index); - ret = accel && accel->apply (c, l.get_subtable_count (), false); + ret = accel && accel->apply (c, false); c->set_lookup_index (saved_lookup_index); c->set_lookup_props (saved_lookup_props); + + hb_swap (c->match_positions, saved_match_positions); + return ret; } #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh index 072e5cdba26..05bbf3395bf 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh @@ -397,303 +397,311 @@ struct hb_collect_coverage_context_t : set_t *set; }; -struct hb_ot_apply_context_t : - hb_dispatch_context_t +struct matcher_t { - struct matcher_t + typedef bool (*match_func_t) (hb_glyph_info_t &info, unsigned value, const void *data); + + template + void init (const context_t *c, bool context_match = false) { - typedef bool (*match_func_t) (hb_glyph_info_t &info, unsigned value, const void *data); + set_match_func (nullptr, nullptr); + lookup_props = c->lookup_props; + /* Ignore ZWNJ if we are matching GPOS, or matching GSUB context and asked to. */ + ignore_zwnj = c->table_index == 1 || (context_match && c->auto_zwnj); + /* Ignore ZWJ if we are matching context, or asked to. */ + ignore_zwj = context_match || c->auto_zwj; + /* Ignore hidden glyphs (like CGJ) during GPOS. */ + ignore_hidden = c->table_index == 1; + mask = context_match ? -1 : c->lookup_mask; + /* Per syllable matching is only for GSUB. */ + per_syllable = c->table_index == 0 && c->per_syllable; + syllable = 0; + } - void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; } - void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; } - void set_ignore_hidden (bool ignore_hidden_) { ignore_hidden = ignore_hidden_; } - void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; } - void set_mask (hb_mask_t mask_) { mask = mask_; } - void set_per_syllable (bool per_syllable_) { per_syllable = per_syllable_; } - void set_syllable (uint8_t syllable_) { syllable = per_syllable ? syllable_ : 0; } - void set_match_func (match_func_t match_func_, - const void *match_data_) - { match_func = match_func_; match_data = match_data_; } + void set_match_func (match_func_t match_func_, + const void *match_data_) + { match_func = match_func_; match_data = match_data_; } - enum may_match_t { - MATCH_NO, - MATCH_YES, - MATCH_MAYBE - }; - -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - may_match_t may_match (hb_glyph_info_t &info, - hb_codepoint_t glyph_data) const - { - if (!(info.mask & mask) || - (syllable && syllable != info.syllable ())) - return MATCH_NO; - - if (match_func) - return match_func (info, glyph_data, match_data) ? MATCH_YES : MATCH_NO; - - return MATCH_MAYBE; - } - - enum may_skip_t { - SKIP_NO, - SKIP_YES, - SKIP_MAYBE - }; - -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - may_skip_t may_skip (const hb_ot_apply_context_t *c, - const hb_glyph_info_t &info) const - { - if (!c->check_glyph_property (&info, lookup_props)) - return SKIP_YES; - - if (unlikely (_hb_glyph_info_is_default_ignorable (&info) && - (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) && - (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) && - (ignore_hidden || !_hb_glyph_info_is_hidden (&info)))) - return SKIP_MAYBE; - - return SKIP_NO; - } - - protected: - unsigned int lookup_props = 0; - hb_mask_t mask = -1; - bool ignore_zwnj = false; - bool ignore_zwj = false; - bool ignore_hidden = false; - bool per_syllable = false; - uint8_t syllable = 0; - match_func_t match_func = nullptr; - const void *match_data = nullptr; + enum may_match_t { + MATCH_NO, + MATCH_YES, + MATCH_MAYBE }; - struct skipping_iterator_t +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + may_match_t may_match (hb_glyph_info_t &info, + hb_codepoint_t glyph_data) const { - void init (hb_ot_apply_context_t *c_, bool context_match = false) - { - c = c_; - end = c->buffer->len; - match_glyph_data16 = nullptr; + if (!(info.mask & mask) || + (per_syllable && syllable && syllable != info.syllable ())) + return MATCH_NO; + + if (match_func) + return match_func (info, glyph_data, match_data) ? MATCH_YES : MATCH_NO; + + return MATCH_MAYBE; + } + + enum may_skip_t { + SKIP_NO, + SKIP_YES, + SKIP_MAYBE + }; + + template +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + may_skip_t may_skip (const context_t *c, + const hb_glyph_info_t &info) const + { + if (!c->check_glyph_property (&info, lookup_props)) + return SKIP_YES; + + if (unlikely (_hb_glyph_info_is_default_ignorable (&info) && + (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) && + (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) && + (ignore_hidden || !_hb_glyph_info_is_hidden (&info)))) + return SKIP_MAYBE; + + return SKIP_NO; + } + + public: + unsigned int lookup_props = 0; + hb_mask_t mask = -1; + bool ignore_zwnj = false; + bool ignore_zwj = false; + bool ignore_hidden = false; + bool per_syllable = false; + uint8_t syllable = 0; + match_func_t match_func = nullptr; + const void *match_data = nullptr; +}; + +template +struct skipping_iterator_t +{ + void init (context_t *c_, bool context_match = false) + { + c = c_; + end = c->buffer->len; + match_glyph_data16 = nullptr; #ifndef HB_NO_BEYOND_64K - match_glyph_data24 = nullptr; + match_glyph_data24 = nullptr; #endif - matcher.set_match_func (nullptr, nullptr); - matcher.set_lookup_props (c->lookup_props); - /* Ignore ZWNJ if we are matching GPOS, or matching GSUB context and asked to. */ - matcher.set_ignore_zwnj (c->table_index == 1 || (context_match && c->auto_zwnj)); - /* Ignore ZWJ if we are matching context, or asked to. */ - matcher.set_ignore_zwj (context_match || c->auto_zwj); - /* Ignore hidden glyphs (like CGJ) during GPOS. */ - matcher.set_ignore_hidden (c->table_index == 1); - matcher.set_mask (context_match ? -1 : c->lookup_mask); - /* Per syllable matching is only for GSUB. */ - matcher.set_per_syllable (c->table_index == 0 && c->per_syllable); - matcher.set_syllable (0); - } - void set_lookup_props (unsigned int lookup_props) - { - matcher.set_lookup_props (lookup_props); - } - void set_match_func (matcher_t::match_func_t match_func_, - const void *match_data_) - { - matcher.set_match_func (match_func_, match_data_); - } - void set_glyph_data (const HBUINT16 glyph_data[]) - { - match_glyph_data16 = glyph_data; + matcher.init (c, context_match); + } + void set_lookup_props (unsigned int lookup_props) + { + matcher.lookup_props = lookup_props; + } + void set_match_func (matcher_t::match_func_t match_func_, + const void *match_data_) + { + matcher.set_match_func (match_func_, match_data_); + } + void set_glyph_data (const HBUINT16 glyph_data[]) + { + match_glyph_data16 = glyph_data; #ifndef HB_NO_BEYOND_64K - match_glyph_data24 = nullptr; + match_glyph_data24 = nullptr; #endif - } + } #ifndef HB_NO_BEYOND_64K - void set_glyph_data (const HBUINT24 glyph_data[]) - { - match_glyph_data16 = nullptr; - match_glyph_data24 = glyph_data; - } + void set_glyph_data (const HBUINT24 glyph_data[]) + { + match_glyph_data16 = nullptr; + match_glyph_data24 = glyph_data; + } #endif #ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE + HB_ALWAYS_INLINE #endif - void reset (unsigned int start_index_) - { - idx = start_index_; - end = c->buffer->len; - matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0); - } - -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - void reset_fast (unsigned int start_index_) - { - // Doesn't set end or syllable. Used by GPOS which doesn't care / change. - idx = start_index_; - } - - void reject () - { - backup_glyph_data (); - } - - matcher_t::may_skip_t -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - may_skip (const hb_glyph_info_t &info) const - { return matcher.may_skip (c, info); } - - enum match_t { - MATCH, - NOT_MATCH, - SKIP - }; - -#ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE -#endif - match_t match (hb_glyph_info_t &info) - { - matcher_t::may_skip_t skip = matcher.may_skip (c, info); - if (unlikely (skip == matcher_t::SKIP_YES)) - return SKIP; - - matcher_t::may_match_t match = matcher.may_match (info, get_glyph_data ()); - if (match == matcher_t::MATCH_YES || - (match == matcher_t::MATCH_MAYBE && - skip == matcher_t::SKIP_NO)) - return MATCH; - - if (skip == matcher_t::SKIP_NO) - return NOT_MATCH; - - return SKIP; + void reset (unsigned int start_index_) + { + // For GSUB forward iterator + idx = start_index_; + end = c->buffer->len; + matcher.syllable = c->buffer->cur().syllable(); + } + void reset_back (unsigned int start_index_, bool from_out_buffer = false) + { + // For GSUB backward iterator + idx = start_index_; + matcher.syllable = c->buffer->cur().syllable(); } #ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE + HB_ALWAYS_INLINE #endif - bool next (unsigned *unsafe_to = nullptr) - { - const signed stop = (signed) end - 1; - while ((signed) idx < stop) - { - idx++; - switch (match (c->buffer->info[idx])) - { - case MATCH: - { - advance_glyph_data (); - return true; - } - case NOT_MATCH: - { - if (unsafe_to) - *unsafe_to = idx + 1; - return false; - } - case SKIP: - continue; - } - } - if (unsafe_to) - *unsafe_to = end; - return false; - } + void reset_fast (unsigned int start_index_) + { + // Doesn't set end or syllable. Used by GPOS which doesn't care / change. + idx = start_index_; + } + #ifndef HB_OPTIMIZE_SIZE - HB_ALWAYS_INLINE + HB_ALWAYS_INLINE #endif - bool prev (unsigned *unsafe_from = nullptr) - { - const unsigned stop = 0; - while (idx > stop) - { - idx--; - switch (match (c->buffer->out_info[idx])) - { - case MATCH: - { - advance_glyph_data (); - return true; - } - case NOT_MATCH: - { - if (unsafe_from) - *unsafe_from = hb_max (1u, idx) - 1u; - return false; - } - case SKIP: - continue; - } - } - if (unsafe_from) - *unsafe_from = 0; - return false; - } + matcher_t::may_skip_t may_skip (const hb_glyph_info_t &info) const + { return matcher.may_skip (c, info); } - HB_ALWAYS_INLINE - hb_codepoint_t - get_glyph_data () - { - if (match_glyph_data16) return *match_glyph_data16; -#ifndef HB_NO_BEYOND_64K - else - if (match_glyph_data24) return *match_glyph_data24; -#endif - return 0; - } - HB_ALWAYS_INLINE - void - advance_glyph_data () - { - if (match_glyph_data16) match_glyph_data16++; -#ifndef HB_NO_BEYOND_64K - else - if (match_glyph_data24) match_glyph_data24++; -#endif - } - void - backup_glyph_data () - { - if (match_glyph_data16) match_glyph_data16--; -#ifndef HB_NO_BEYOND_64K - else - if (match_glyph_data24) match_glyph_data24--; -#endif - } - - unsigned int idx; - protected: - hb_ot_apply_context_t *c; - matcher_t matcher; - const HBUINT16 *match_glyph_data16; -#ifndef HB_NO_BEYOND_64K - const HBUINT24 *match_glyph_data24; -#endif - - unsigned int end; + enum match_t { + MATCH, + NOT_MATCH, + SKIP }; +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + match_t match (hb_glyph_info_t &info) + { + matcher_t::may_skip_t skip = matcher.may_skip (c, info); + if (unlikely (skip == matcher_t::SKIP_YES)) + return SKIP; + matcher_t::may_match_t match = matcher.may_match (info, get_glyph_data ()); + if (match == matcher_t::MATCH_YES || + (match == matcher_t::MATCH_MAYBE && + skip == matcher_t::SKIP_NO)) + return MATCH; + + if (skip == matcher_t::SKIP_NO) + return NOT_MATCH; + + return SKIP; + } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool next (unsigned *unsafe_to = nullptr) + { + auto *info = c->buffer->info; + const signed stop = (signed) end - 1; + while ((signed) idx < stop) + { + idx++; + switch (match (info[idx])) + { + case MATCH: + { + advance_glyph_data (); + return true; + } + case NOT_MATCH: + { + if (unsafe_to) + *unsafe_to = idx + 1; + return false; + } + case SKIP: + continue; + } + } + if (unsafe_to) + *unsafe_to = end; + return false; + } +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool prev (unsigned *unsafe_from = nullptr) + { + auto *out_info = c->buffer->out_info; + const unsigned stop = 0; + while (idx > stop) + { + idx--; + switch (match (out_info[idx])) + { + case MATCH: + { + advance_glyph_data (); + return true; + } + case NOT_MATCH: + { + if (unsafe_from) + *unsafe_from = hb_max (1u, idx) - 1u; + return false; + } + case SKIP: + continue; + } + } + if (unsafe_from) + *unsafe_from = 0; + return false; + } + + HB_ALWAYS_INLINE + hb_codepoint_t + get_glyph_data () + { + if (match_glyph_data16) return *match_glyph_data16; +#ifndef HB_NO_BEYOND_64K + else + if (match_glyph_data24) return *match_glyph_data24; +#endif + return 0; + } + HB_ALWAYS_INLINE + void + advance_glyph_data () + { + if (match_glyph_data16) match_glyph_data16++; +#ifndef HB_NO_BEYOND_64K + else + if (match_glyph_data24) match_glyph_data24++; +#endif + } + + unsigned int idx; + protected: + context_t *c; + matcher_t matcher; + const HBUINT16 *match_glyph_data16; +#ifndef HB_NO_BEYOND_64K + const HBUINT24 *match_glyph_data24; +#endif + + unsigned int end; +}; + +struct hb_ot_apply_context_t : + hb_dispatch_context_t +{ const char *get_name () { return "APPLY"; } typedef return_t (*recurse_func_t) (hb_ot_apply_context_t *c, unsigned int lookup_index); + template - return_t dispatch (const T &obj) { return obj.apply (this); } + static inline auto apply_ (const T &obj, hb_ot_apply_context_t *c, hb_priority<1>) HB_RETURN (return_t, obj.apply (c, nullptr) ) + template + static inline auto apply_ (const T &obj, hb_ot_apply_context_t *c, hb_priority<0>) HB_RETURN (return_t, obj.apply (c) ) + template + return_t dispatch (const T &obj) { return apply_(obj, this, hb_prioritize); } + static return_t default_return_value () { return false; } bool stop_sublookup_iteration (return_t r) const { return r; } return_t recurse (unsigned int sub_lookup_index) { - if (unlikely (nesting_level_left == 0 || !recurse_func || buffer->max_ops-- <= 0)) + assert (recurse_func); + if (unlikely (nesting_level_left == 0)) { - buffer->shaping_failed = true; + buffer->successful = false; + return default_return_value (); + } + + buffer->max_ops--; + if (unlikely (buffer->max_ops < 0)) + { + buffer->successful = false; return default_return_value (); } @@ -703,7 +711,7 @@ struct hb_ot_apply_context_t : return ret; } - skipping_iterator_t iter_input, iter_context; + skipping_iterator_t iter_input, iter_context; unsigned int table_index; /* GSUB/GPOS */ hb_font_t *font; @@ -715,8 +723,7 @@ struct hb_ot_apply_context_t : const GDEF::accelerator_t &gdef_accel; const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr; const ItemVariationStore &var_store; - ItemVariationStore::cache_t *var_store_cache; - hb_set_digest_t digest; + hb_scalar_cache_t *var_store_cache; hb_direction_t direction; hb_mask_t lookup_mask = 1; @@ -734,11 +741,14 @@ struct hb_ot_apply_context_t : signed last_base = -1; // GPOS uses unsigned last_base_until = 0; // GPOS uses + hb_vector_t match_positions; + uint32_t stack_match_positions[8]; + hb_ot_apply_context_t (unsigned int table_index_, hb_font_t *font_, hb_buffer_t *buffer_, hb_blob_t *table_blob_, - ItemVariationStore::cache_t *var_store_cache_ = nullptr) : + hb_scalar_cache_t *var_store_cache_ = nullptr) : table_index (table_index_), font (font_), face (font->face), buffer (buffer_), sanitizer (table_blob_), @@ -762,7 +772,7 @@ struct hb_ot_apply_context_t : has_glyph_classes (gdef.has_glyph_classes ()) { init_iters (); - buffer->collect_codepoints (digest); + match_positions.set_storage (stack_match_positions); } void init_iters () @@ -778,7 +788,11 @@ struct hb_ot_apply_context_t : void set_random (bool random_) { random = random_; } void set_recurse_func (recurse_func_t func) { recurse_func = func; } void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; } - void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; init_iters (); } + void set_lookup_props (unsigned int lookup_props_) + { + lookup_props = gdef_accel.sanitize_lookup_props (lookup_props_); + init_iters (); + } uint32_t random_number () { @@ -787,7 +801,9 @@ struct hb_ot_apply_context_t : return buffer->random_state; } - bool match_properties_mark (hb_codepoint_t glyph, + HB_ALWAYS_INLINE + HB_HOT + bool match_properties_mark (const hb_glyph_info_t *info, unsigned int glyph_props, unsigned int match_props) const { @@ -795,7 +811,7 @@ struct hb_ot_apply_context_t : * match_props has the set index. */ if (match_props & LookupFlag::UseMarkFilteringSet) - return gdef_accel.mark_set_covers (match_props >> 16, glyph); + return gdef_accel.mark_set_covers (match_props >> 16, info->codepoint); /* The second byte of match_props has the meaning * "ignore marks of attachment type different than @@ -811,7 +827,7 @@ struct hb_ot_apply_context_t : HB_ALWAYS_INLINE #endif bool check_glyph_property (const hb_glyph_info_t *info, - unsigned int match_props) const + unsigned match_props) const { unsigned int glyph_props = _hb_glyph_info_get_glyph_props (info); @@ -821,8 +837,8 @@ struct hb_ot_apply_context_t : if (glyph_props & match_props & LookupFlag::IgnoreFlags) return false; - if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) - return match_properties_mark (info->codepoint, glyph_props, match_props); + if (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK) + return match_properties_mark (info, glyph_props, match_props); return true; } @@ -832,7 +848,7 @@ struct hb_ot_apply_context_t : bool ligature = false, bool component = false) { - digest.add (glyph_index); + buffer->digest.add (glyph_index); if (new_syllables != (unsigned) -1) buffer->cur().syllable() = new_syllables; @@ -890,54 +906,58 @@ struct hb_ot_apply_context_t : } }; -enum class hb_ot_lookup_cache_op_t +enum class hb_ot_subtable_cache_op_t { - CREATE, ENTER, LEAVE, - DESTROY, }; struct hb_accelerate_subtables_context_t : hb_dispatch_context_t { - template - static inline bool apply_to (const void *obj, hb_ot_apply_context_t *c) + template + static inline auto apply_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<1>) HB_RETURN (bool, obj->apply (c, external_cache) ) + template + static inline auto apply_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) + template + static inline bool apply_to (const void *obj, hb_ot_apply_context_t *c, void *external_cache) { - const Type *typed_obj = (const Type *) obj; - return typed_obj->apply (c); + const T *typed_obj = (const T *) obj; + return apply_ (typed_obj, c, external_cache, hb_prioritize); } #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE template - static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, hb_priority<1>) HB_RETURN (bool, obj->apply_cached (c) ) + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<2>) HB_RETURN (bool, obj->apply_cached (c, external_cache) ) template - static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) - template - static inline bool apply_cached_to (const void *obj, hb_ot_apply_context_t *c) + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<1>) HB_RETURN (bool, obj->apply (c, external_cache) ) + template + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, void *external_cache, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) + template + static inline bool apply_cached_to (const void *obj, hb_ot_apply_context_t *c, void *external_cache) { - const Type *typed_obj = (const Type *) obj; - return apply_cached_ (typed_obj, c, hb_prioritize); + const T *typed_obj = (const T *) obj; + return apply_cached_ (typed_obj, c, external_cache, hb_prioritize); } template - static inline auto cache_func_ (void *p, - hb_ot_lookup_cache_op_t op, - hb_priority<1>) HB_RETURN (void *, T::cache_func (p, op) ) + static inline auto cache_func_ (hb_ot_apply_context_t *c, + hb_ot_subtable_cache_op_t op, + hb_priority<1>) HB_RETURN (bool, T::cache_func (c, op) ) template - static inline void * cache_func_ (void *p, - hb_ot_lookup_cache_op_t op HB_UNUSED, - hb_priority<0>) { return (void *) false; } + static inline bool cache_func_ (hb_ot_apply_context_t *c, + hb_ot_subtable_cache_op_t op HB_UNUSED, + hb_priority<0>) { return false; } template - static inline void * cache_func_to (void *p, - hb_ot_lookup_cache_op_t op) + static inline bool cache_func_to (hb_ot_apply_context_t *c, + hb_ot_subtable_cache_op_t op) { - return cache_func_ (p, op, hb_prioritize); + return cache_func_ (c, op, hb_prioritize); } #endif - typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c); - typedef void * (*hb_cache_func_t) (void *p, hb_ot_lookup_cache_op_t op); + typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c, void *external_cache); + typedef bool (*hb_cache_func_t) (hb_ot_apply_context_t *c, hb_ot_subtable_cache_op_t op); struct hb_applicable_t { @@ -950,6 +970,7 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE , hb_apply_func_t apply_cached_func_ , hb_cache_func_t cache_func_ + , void *external_cache_ #endif ) { @@ -958,27 +979,34 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE apply_cached_func = apply_cached_func_; cache_func = cache_func_; + external_cache = external_cache_; #endif digest.init (); obj_.get_coverage ().collect_coverage (&digest); } +#ifdef HB_NO_OT_LAYOUT_LOOKUP_CACHE bool apply (hb_ot_apply_context_t *c) const { - return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c); + return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c, nullptr); + } +#else + bool apply (hb_ot_apply_context_t *c) const + { + return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c, external_cache); } -#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE bool apply_cached (hb_ot_apply_context_t *c) const { - return digest.may_have (c->buffer->cur().codepoint) && apply_cached_func (obj, c); + return digest.may_have (c->buffer->cur().codepoint) && apply_cached_func (obj, c, external_cache); } + bool cache_enter (hb_ot_apply_context_t *c) const { - return (bool) cache_func (c, hb_ot_lookup_cache_op_t::ENTER); + return cache_func (c, hb_ot_subtable_cache_op_t::ENTER); } void cache_leave (hb_ot_apply_context_t *c) const { - cache_func (c, hb_ot_lookup_cache_op_t::LEAVE); + cache_func (c, hb_ot_subtable_cache_op_t::LEAVE); } #endif @@ -988,6 +1016,7 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE hb_apply_func_t apply_cached_func; hb_cache_func_t cache_func; + void *external_cache; #endif hb_set_digest_t digest; }; @@ -997,12 +1026,23 @@ struct hb_accelerate_subtables_context_t : auto cache_cost (const T &obj, hb_priority<1>) HB_AUTO_RETURN ( obj.cache_cost () ) template auto cache_cost (const T &obj, hb_priority<0>) HB_AUTO_RETURN ( 0u ) + + template + auto external_cache_create (const T &obj, hb_priority<1>) HB_AUTO_RETURN ( obj.external_cache_create () ) + template + auto external_cache_create (const T &obj, hb_priority<0>) HB_AUTO_RETURN ( nullptr ) #endif /* Dispatch interface. */ template return_t dispatch (const T &obj) { +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + void *external_cache = nullptr; + if (i < 8) + external_cache = external_cache_create (obj, hb_prioritize); +#endif + hb_applicable_t *entry = &array[i++]; entry->init (obj, @@ -1010,6 +1050,7 @@ struct hb_accelerate_subtables_context_t : #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE , apply_cached_to , cache_func_to + , external_cache #endif ); @@ -1023,10 +1064,10 @@ struct hb_accelerate_subtables_context_t : * and we allocate the cache opportunity to the costliest subtable. */ unsigned cost = cache_cost (obj, hb_prioritize); - if (cost > cache_user_cost) + if (cost > subtable_cache_user_cost) { - cache_user_idx = i - 1; - cache_user_cost = cost; + subtable_cache_user_idx = i - 1; + subtable_cache_user_cost = cost; } #endif @@ -1041,8 +1082,8 @@ struct hb_accelerate_subtables_context_t : unsigned i = 0; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - unsigned cache_user_idx = (unsigned) -1; - unsigned cache_user_cost = 0; + unsigned subtable_cache_user_idx = (unsigned) -1; + unsigned subtable_cache_user_cost = 0; #endif }; @@ -1191,38 +1232,50 @@ static inline bool match_class (hb_glyph_info_t &info, unsigned value, const voi const ClassDef &class_def = *reinterpret_cast(data); return class_def.get_class (info.codepoint) == value; } -static inline bool match_class_cached (hb_glyph_info_t &info, unsigned value, const void *data) +static inline unsigned get_class_cached (const ClassDef &class_def, hb_glyph_info_t &info) { unsigned klass = info.syllable(); if (klass < 255) - return klass == value; - const ClassDef &class_def = *reinterpret_cast(data); + return klass; klass = class_def.get_class (info.codepoint); if (likely (klass < 255)) info.syllable() = klass; - return klass == value; + return klass; } -static inline bool match_class_cached1 (hb_glyph_info_t &info, unsigned value, const void *data) +static inline bool match_class_cached (hb_glyph_info_t &info, unsigned value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast(data); + return get_class_cached (class_def, info) == value; +} +static inline unsigned get_class_cached1 (const ClassDef &class_def, hb_glyph_info_t &info) { unsigned klass = info.syllable() & 0x0F; if (klass < 15) - return klass == value; - const ClassDef &class_def = *reinterpret_cast(data); + return klass; klass = class_def.get_class (info.codepoint); if (likely (klass < 15)) info.syllable() = (info.syllable() & 0xF0) | klass; - return klass == value; + return klass; } -static inline bool match_class_cached2 (hb_glyph_info_t &info, unsigned value, const void *data) +static inline bool match_class_cached1 (hb_glyph_info_t &info, unsigned value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast(data); + return get_class_cached1 (class_def, info) == value; +} +static inline unsigned get_class_cached2 (const ClassDef &class_def, hb_glyph_info_t &info) { unsigned klass = (info.syllable() & 0xF0) >> 4; if (klass < 15) - return klass == value; - const ClassDef &class_def = *reinterpret_cast(data); + return klass; klass = class_def.get_class (info.codepoint); if (likely (klass < 15)) info.syllable() = (info.syllable() & 0x0F) | (klass << 4); - return klass == value; + return klass; +} +static inline bool match_class_cached2 (hb_glyph_info_t &info, unsigned value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast(data); + return get_class_cached2 (class_def, info) == value; } static inline bool match_coverage (hb_glyph_info_t &info, unsigned value, const void *data) { @@ -1261,16 +1314,24 @@ static bool match_input (hb_ot_apply_context_t *c, match_func_t match_func, const void *match_data, unsigned int *end_position, - unsigned int *match_positions, unsigned int *p_total_component_count = nullptr) { TRACE_APPLY (nullptr); - if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false); - hb_buffer_t *buffer = c->buffer; - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + if (count == 1) + { + *end_position = buffer->idx + 1; + c->match_positions[0] = buffer->idx; + if (p_total_component_count) + *p_total_component_count = _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + return_trace (true); + } + + if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false); + + auto &skippy_iter = c->iter_input; skippy_iter.reset (buffer->idx); skippy_iter.set_match_func (match_func, match_data); skippy_iter.set_glyph_data (input); @@ -1319,7 +1380,10 @@ static bool match_input (hb_ot_apply_context_t *c, return_trace (false); } - match_positions[i] = skippy_iter.idx; + if (unlikely (i + 1 > c->match_positions.length && + !c->match_positions.resize_dirty (i + 1))) + return_trace (false); + c->match_positions.arrayZ[i] = skippy_iter.idx; unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]); unsigned int this_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]); @@ -1349,7 +1413,7 @@ static bool match_input (hb_ot_apply_context_t *c, j--; } - if (found && skippy_iter.may_skip (out[j]) == hb_ot_apply_context_t::matcher_t::SKIP_YES) + if (found && skippy_iter.may_skip (out[j]) == matcher_t::SKIP_YES) ligbase = LIGBASE_MAY_SKIP; else ligbase = LIGBASE_MAY_NOT_SKIP; @@ -1379,13 +1443,12 @@ static bool match_input (hb_ot_apply_context_t *c, *p_total_component_count = total_component_count; } - match_positions[0] = buffer->idx; + c->match_positions.arrayZ[0] = buffer->idx; return_trace (true); } static inline bool ligate_input (hb_ot_apply_context_t *c, unsigned int count, /* Including the first glyph */ - const unsigned int *match_positions, /* Including the first glyph */ unsigned int match_end, hb_codepoint_t lig_glyph, unsigned int total_component_count) @@ -1428,10 +1491,10 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, * https://bugzilla.gnome.org/show_bug.cgi?id=437633 */ - bool is_base_ligature = _hb_glyph_info_is_base_glyph (&buffer->info[match_positions[0]]); - bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->info[match_positions[0]]); + bool is_base_ligature = _hb_glyph_info_is_base_glyph (&buffer->info[c->match_positions.arrayZ[0]]); + bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->info[c->match_positions.arrayZ[0]]); for (unsigned int i = 1; i < count; i++) - if (!_hb_glyph_info_is_mark (&buffer->info[match_positions[i]])) + if (!_hb_glyph_info_is_mark (&buffer->info[c->match_positions.arrayZ[i]])) { is_base_ligature = false; is_mark_ligature = false; @@ -1457,7 +1520,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, for (unsigned int i = 1; i < count; i++) { - while (buffer->idx < match_positions[i] && buffer->successful) + while (buffer->idx < c->match_positions.arrayZ[i] && buffer->successful) { if (is_ligature) { @@ -1512,8 +1575,14 @@ static bool match_backtrack (hb_ot_apply_context_t *c, { TRACE_APPLY (nullptr); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; - skippy_iter.reset (c->buffer->backtrack_len ()); + if (!count) + { + *match_start = c->buffer->backtrack_len (); + return_trace (true); + } + + auto &skippy_iter = c->iter_context; + skippy_iter.reset_back (c->buffer->backtrack_len ()); skippy_iter.set_match_func (match_func, match_data); skippy_iter.set_glyph_data (backtrack); @@ -1545,7 +1614,13 @@ static bool match_lookahead (hb_ot_apply_context_t *c, { TRACE_APPLY (nullptr); - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; + if (!count) + { + *end_index = start_index; + return_trace (true); + } + + auto &skippy_iter = c->iter_context; assert (start_index >= 1); skippy_iter.reset (start_index - 1); skippy_iter.set_match_func (match_func, match_data); @@ -1696,7 +1771,6 @@ static inline void recurse_lookups (context_t *c, static inline void apply_lookup (hb_ot_apply_context_t *c, unsigned int count, /* Including the first glyph */ - unsigned int *match_positions, /* Including the first glyph */ unsigned int lookupCount, const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ unsigned int match_end) @@ -1704,9 +1778,6 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, hb_buffer_t *buffer = c->buffer; int end; - unsigned int *match_positions_input = match_positions; - unsigned int match_positions_count = count; - /* All positions are distance from beginning of *output* buffer. * Adjust. */ { @@ -1716,7 +1787,7 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, int delta = bl - buffer->idx; /* Convert positions to new indexing. */ for (unsigned int j = 0; j < count; j++) - match_positions[j] += delta; + c->match_positions.arrayZ[j] += delta; } for (unsigned int i = 0; i < lookupCount && buffer->successful; i++) @@ -1728,10 +1799,10 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, unsigned int orig_len = buffer->backtrack_len () + buffer->lookahead_len (); /* This can happen if earlier recursed lookups deleted many entries. */ - if (unlikely (match_positions[idx] >= orig_len)) + if (unlikely (c->match_positions.arrayZ[idx] >= orig_len)) continue; - if (unlikely (!buffer->move_to (match_positions[idx]))) + if (unlikely (!buffer->move_to (c->match_positions.arrayZ[idx]))) break; if (unlikely (buffer->max_ops <= 0)) @@ -1790,9 +1861,9 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, */ end += delta; - if (end < int (match_positions[idx])) + if (end < int (c->match_positions.arrayZ[idx])) { - /* End might end up being smaller than match_positions[idx] if the recursed + /* End might end up being smaller than match_positions.arrayZ[idx] if the recursed * lookup ended up removing many items. * Just never rewind end beyond start of current position, since that is * not possible in the recursed lookup. Also adjust delta as such. @@ -1800,8 +1871,8 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 * https://github.com/harfbuzz/harfbuzz/issues/1611 */ - delta += match_positions[idx] - end; - end = match_positions[idx]; + delta += c->match_positions.arrayZ[idx] - end; + end = c->match_positions.arrayZ[idx]; } unsigned int next = idx + 1; /* next now is the position after the recursed lookup. */ @@ -1810,27 +1881,9 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, { if (unlikely (delta + count > HB_MAX_CONTEXT_LENGTH)) break; - if (unlikely (delta + count > match_positions_count)) - { - unsigned new_match_positions_count = hb_max (delta + count, hb_max(match_positions_count, 4u) * 1.5); - if (match_positions == match_positions_input) - { - match_positions = (unsigned int *) hb_malloc (new_match_positions_count * sizeof (match_positions[0])); - if (unlikely (!match_positions)) - break; - memcpy (match_positions, match_positions_input, count * sizeof (match_positions[0])); - match_positions_count = new_match_positions_count; - } - else - { - unsigned int *new_match_positions = (unsigned int *) hb_realloc (match_positions, new_match_positions_count * sizeof (match_positions[0])); - if (unlikely (!new_match_positions)) - break; - match_positions = new_match_positions; - match_positions_count = new_match_positions_count; - } - } - + if (unlikely (count + delta > c->match_positions.length && + !c->match_positions.resize_dirty (count + delta))) + return; } else { @@ -1840,23 +1893,20 @@ static inline void apply_lookup (hb_ot_apply_context_t *c, } /* Shift! */ - memmove (match_positions + next + delta, match_positions + next, - (count - next) * sizeof (match_positions[0])); + memmove (c->match_positions + next + delta, c->match_positions + next, + (count - next) * sizeof (c->match_positions.arrayZ[0])); next += delta; count += delta; /* Fill in new entries. */ for (unsigned int j = idx + 1; j < next; j++) - match_positions[j] = match_positions[j - 1] + 1; + c->match_positions.arrayZ[j] = c->match_positions.arrayZ[j - 1] + 1; /* And fixup the rest. */ for (; next < count; next++) - match_positions[next] += delta; + c->match_positions.arrayZ[next] += delta; } - if (match_positions != match_positions_input) - hb_free (match_positions); - assert (end >= 0); (void) buffer->move_to (end); } @@ -1950,34 +2000,25 @@ static inline bool context_would_apply_lookup (hb_would_apply_context_t *c, } template -HB_ALWAYS_INLINE -static bool context_apply_lookup (hb_ot_apply_context_t *c, - unsigned int inputCount, /* Including the first glyph (not matched) */ - const HBUINT input[], /* Array of input values--start with second glyph */ - unsigned int lookupCount, - const LookupRecord lookupRecord[], - const ContextApplyLookupContext &lookup_context) +static inline bool context_apply_lookup (hb_ot_apply_context_t *c, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], + const ContextApplyLookupContext &lookup_context) { if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) return false; - unsigned match_positions_stack[4]; - unsigned *match_positions = match_positions_stack; - if (unlikely (inputCount > ARRAY_LENGTH (match_positions_stack))) - { - match_positions = (unsigned *) hb_malloc (hb_max (inputCount, 1u) * sizeof (match_positions[0])); - if (unlikely (!match_positions)) - return false; - } unsigned match_end = 0; bool ret = false; if (match_input (c, inputCount, input, lookup_context.funcs.match, lookup_context.match_data, - &match_end, match_positions)) + &match_end)) { c->buffer->unsafe_to_break (c->buffer->idx, match_end); apply_lookup (c, - inputCount, match_positions, + inputCount, lookupCount, lookupRecord, match_end); ret = true; @@ -1988,12 +2029,34 @@ static bool context_apply_lookup (hb_ot_apply_context_t *c, ret = false; } - if (unlikely (match_positions != match_positions_stack)) - hb_free (match_positions); - return ret; } +static inline bool context_cache_func (hb_ot_apply_context_t *c, hb_ot_subtable_cache_op_t op) +{ + switch (op) + { + case hb_ot_subtable_cache_op_t::ENTER: + { + if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) + return false; + auto &info = c->buffer->info; + unsigned count = c->buffer->len; + for (unsigned i = 0; i < count; i++) + info[i].syllable() = 255; + c->new_syllables = 255; + return true; + } + case hb_ot_subtable_cache_op_t::LEAVE: + { + c->new_syllables = (unsigned) -1; + HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); + break; + } + } + return false; +} + template struct Rule { @@ -2209,24 +2272,29 @@ struct RuleSet * * Replicated from LigatureSet::apply(). */ - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + /* We use the iter_context instead of iter_input, to avoid skipping + * default-ignorables and such. + * + * Related: https://github.com/harfbuzz/harfbuzz/issues/4813 + */ + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); - unsigned unsafe_to = (unsigned) -1, unsafe_to1 = 0, unsafe_to2 = 0; + unsigned unsafe_to = (unsigned) -1, unsafe_to1, unsafe_to2 = 0; hb_glyph_info_t *first = nullptr, *second = nullptr; bool matched = skippy_iter.next (); if (likely (matched)) { - first = &c->buffer->info[skippy_iter.idx]; - unsafe_to = skippy_iter.idx + 1; - if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) { /* Can't use the fast path if eg. the next char is a default-ignorable * or other skippable. */ goto slow; } + + first = &c->buffer->info[skippy_iter.idx]; + unsafe_to1 = skippy_iter.idx + 1; } else { @@ -2242,8 +2310,15 @@ struct RuleSet ; } matched = skippy_iter.next (); - if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) + if (likely (matched)) { + if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) + { + /* Can't use the fast path if eg. the next char is a default-ignorable + * or other skippable. */ + goto slow; + } + second = &c->buffer->info[skippy_iter.idx]; unsafe_to2 = skippy_iter.idx + 1; } @@ -2280,6 +2355,15 @@ struct RuleSet { if (unsafe_to == (unsigned) -1) unsafe_to = unsafe_to1; + + // Skip ahead to next possible first glyph match. + for (; i + 1 < num_rules; i++) + { + const auto &r2 = this+rule.arrayZ[i + 1]; + const auto &input2 = r2.inputZ; + if (r2.inputCount <= 1 || input2.arrayZ[0] != input.arrayZ[0]) + break; + } } } if (likely (unsafe_to != (unsigned) -1)) @@ -2622,46 +2706,37 @@ struct ContextFormat2_5 unsigned cache_cost () const { - unsigned c = (this+classDef).cost () * ruleSet.len; - return c >= 4 ? c : 0; + return (this+classDef).cost (); } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) + static bool cache_func (hb_ot_apply_context_t *c, hb_ot_subtable_cache_op_t op) { - switch (op) - { - case hb_ot_lookup_cache_op_t::CREATE: - return (void *) true; - case hb_ot_lookup_cache_op_t::ENTER: - { - hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; - if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) - return (void *) false; - auto &info = c->buffer->info; - unsigned count = c->buffer->len; - for (unsigned i = 0; i < count; i++) - info[i].syllable() = 255; - c->new_syllables = 255; - return (void *) true; - } - case hb_ot_lookup_cache_op_t::LEAVE: - { - hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; - c->new_syllables = (unsigned) -1; - HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); - return nullptr; - } - case hb_ot_lookup_cache_op_t::DESTROY: - return nullptr; - } - return nullptr; + return context_cache_func (c, op); } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + struct external_cache_t + { + hb_ot_layout_binary_cache_t coverage; + }; + void *external_cache_create () const + { + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) + { + cache->coverage.clear (); + } + return cache; + } + bool apply_cached (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, true, external_cache); } + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, false, external_cache); } + bool _apply (hb_ot_apply_context_t *c, bool cached, void *external_cache) const { TRACE_APPLY (this); +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + external_cache_t *cache = (external_cache_t *) external_cache; + unsigned int index = (this+coverage).get_coverage_binary (c->buffer->cur().codepoint, cache ? &cache->coverage : nullptr); +#else unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); +#endif if (index == NOT_COVERED) return_trace (false); const ClassDef &class_def = this+classDef; @@ -2671,10 +2746,7 @@ struct ContextFormat2_5 &class_def }; - if (cached && c->buffer->cur().syllable() < 255) - index = c->buffer->cur().syllable (); - else - index = class_def.get_class (c->buffer->cur().codepoint); + index = cached ? get_class_cached (class_def, c->buffer->cur()) : class_def.get_class (c->buffer->cur().codepoint); const RuleSet &rule_set = this+ruleSet[index]; return_trace (rule_set.apply (c, lookup_context)); } @@ -2919,9 +2991,9 @@ struct Context template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -2935,7 +3007,7 @@ struct Context protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ContextFormat1_4 format1; ContextFormat2_5 format2; ContextFormat3 format3; @@ -3069,27 +3141,18 @@ static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c } template -HB_ALWAYS_INLINE -static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, - unsigned int backtrackCount, - const HBUINT backtrack[], - unsigned int inputCount, /* Including the first glyph (not matched) */ - const HBUINT input[], /* Array of input values--start with second glyph */ - unsigned int lookaheadCount, - const HBUINT lookahead[], - unsigned int lookupCount, - const LookupRecord lookupRecord[], - const ChainContextApplyLookupContext &lookup_context) +static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c, + unsigned int backtrackCount, + const HBUINT backtrack[], + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const HBUINT lookahead[], + unsigned int lookupCount, + const LookupRecord lookupRecord[], + const ChainContextApplyLookupContext &lookup_context) { if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) return false; - unsigned match_positions_stack[4]; - unsigned *match_positions = match_positions_stack; - if (unlikely (inputCount > ARRAY_LENGTH (match_positions_stack))) - { - match_positions = (unsigned *) hb_malloc (hb_max (inputCount, 1u) * sizeof (match_positions[0])); - if (unlikely (!match_positions)) - return false; - } unsigned start_index = c->buffer->out_len; unsigned end_index = c->buffer->idx; @@ -3098,15 +3161,14 @@ static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, if (!(match_input (c, inputCount, input, lookup_context.funcs.match[1], lookup_context.match_data[1], - &match_end, match_positions) && (end_index = match_end) + &match_end) && (end_index = match_end) && match_lookahead (c, lookaheadCount, lookahead, lookup_context.funcs.match[2], lookup_context.match_data[2], match_end, &end_index))) { c->buffer->unsafe_to_concat (c->buffer->idx, end_index); - ret = false; - goto done; + return false; } if (!match_backtrack (c, @@ -3115,19 +3177,14 @@ static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, &start_index)) { c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index); - ret = false; - goto done; + return false; } c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index); apply_lookup (c, - inputCount, match_positions, + inputCount, lookupCount, lookupRecord, match_end); - done: - - if (unlikely (match_positions != match_positions_stack)) - hb_free (match_positions); return ret; } @@ -3411,33 +3468,29 @@ struct ChainRuleSet * * Replicated from LigatureSet::apply(). */ - /* If the input skippy has non-auto joiners behavior (as in Indic shapers), - * skip this fast path, as we don't distinguish between input & lookahead - * matching in the fast path. + /* We use the iter_context instead of iter_input, to avoid skipping + * default-ignorables and such. * - * https://github.com/harfbuzz/harfbuzz/issues/4813 + * Related: https://github.com/harfbuzz/harfbuzz/issues/4813 */ - if (!c->auto_zwnj || !c->auto_zwj) - goto slow; - - hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); - unsigned unsafe_to = (unsigned) -1, unsafe_to1 = 0, unsafe_to2 = 0; + unsigned unsafe_to = (unsigned) -1, unsafe_to1, unsafe_to2 = 0; hb_glyph_info_t *first = nullptr, *second = nullptr; bool matched = skippy_iter.next (); if (likely (matched)) { - first = &c->buffer->info[skippy_iter.idx]; - unsafe_to1 = skippy_iter.idx + 1; - if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) { /* Can't use the fast path if eg. the next char is a default-ignorable * or other skippable. */ goto slow; } + + first = &c->buffer->info[skippy_iter.idx]; + unsafe_to1 = skippy_iter.idx + 1; } else { @@ -3458,8 +3511,15 @@ struct ChainRuleSet ; } matched = skippy_iter.next (); - if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) + if (likely (matched)) { + if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) + { + /* Can't use the fast path if eg. the next char is a default-ignorable + * or other skippable. */ + goto slow; + } + second = &c->buffer->info[skippy_iter.idx]; unsafe_to2 = skippy_iter.idx + 1; } @@ -3475,7 +3535,7 @@ struct ChainRuleSet const auto &input = StructAfter (r.backtrack); const auto &lookahead = StructAfter (input); - unsigned lenP1 = hb_max ((unsigned) input.lenP1, 1u); + unsigned lenP1 = input.lenP1; if (lenP1 > 1 ? (!match_input || match_input (*first, input.arrayZ[0], input_data)) @@ -3483,6 +3543,7 @@ struct ChainRuleSet (!lookahead.len || !match_lookahead || match_lookahead (*first, lookahead.arrayZ[0], lookahead_data))) { + lenP1 = hb_max (lenP1, 1u); if (!second || (lenP1 > 2 ? (!match_input || @@ -3505,6 +3566,18 @@ struct ChainRuleSet { if (unsafe_to == (unsigned) -1) unsafe_to = unsafe_to1; + + if (lenP1 > 1) + { + // Skip ahead to next possible first glyph match. + for (; i + 1 < num_rules; i++) + { + const auto &r2 = this+rule.arrayZ[i + 1]; + const auto &input2 = StructAfter (r2.backtrack); + if (input2.lenP1 <= 1 || input2.arrayZ[0] != input.arrayZ[0]) + break; + } + } } } if (likely (unsafe_to != (unsigned) -1)) @@ -3873,45 +3946,37 @@ struct ChainContextFormat2_5 unsigned cache_cost () const { - return (this+lookaheadClassDef).cost () * ruleSet.len; + return (this+inputClassDef).cost () + (this+lookaheadClassDef).cost (); } - static void * cache_func (void *p, hb_ot_lookup_cache_op_t op) + static bool cache_func (hb_ot_apply_context_t *c, hb_ot_subtable_cache_op_t op) { - switch (op) - { - case hb_ot_lookup_cache_op_t::CREATE: - return (void *) true; - case hb_ot_lookup_cache_op_t::ENTER: - { - hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; - if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) - return (void *) false; - auto &info = c->buffer->info; - unsigned count = c->buffer->len; - for (unsigned i = 0; i < count; i++) - info[i].syllable() = 255; - c->new_syllables = 255; - return (void *) true; - } - case hb_ot_lookup_cache_op_t::LEAVE: - { - hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p; - c->new_syllables = (unsigned) -1; - HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); - return nullptr; - } - case hb_ot_lookup_cache_op_t::DESTROY: - return nullptr; - } - return nullptr; + return context_cache_func (c, op); } - bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + struct external_cache_t + { + hb_ot_layout_binary_cache_t coverage; + }; + void *external_cache_create () const + { + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) + { + cache->coverage.clear (); + } + return cache; + } + bool apply_cached (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, true, external_cache); } + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, false, external_cache); } + bool _apply (hb_ot_apply_context_t *c, bool cached, void *external_cache) const { TRACE_APPLY (this); +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + external_cache_t *cache = (external_cache_t *) external_cache; + unsigned int index = (this+coverage).get_coverage_binary (c->buffer->cur().codepoint, cache ? &cache->coverage : nullptr); +#else unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); +#endif if (index == NOT_COVERED) return_trace (false); const ClassDef &backtrack_class_def = this+backtrackClassDef; @@ -3929,11 +3994,9 @@ struct ChainContextFormat2_5 &lookahead_class_def} }; - // Note: Corresponds to match_class_cached2 - if (cached && ((c->buffer->cur().syllable() & 0xF0) >> 4) < 15) - index = (c->buffer->cur().syllable () & 0xF0) >> 4; - else - index = input_class_def.get_class (c->buffer->cur().codepoint); + index = cached + ? get_class_cached2 (input_class_def, c->buffer->cur()) + : input_class_def.get_class (c->buffer->cur().codepoint); const ChainRuleSet &rule_set = this+ruleSet[index]; return_trace (rule_set.apply (c, lookup_context)); } @@ -4261,9 +4324,9 @@ struct ChainContext template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -4277,7 +4340,7 @@ struct ChainContext protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ChainContextFormat1_4 format1; ChainContextFormat2_5 format2; ChainContextFormat3 format3; @@ -4352,7 +4415,7 @@ struct Extension { unsigned int get_type () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_type (); default:return 0; } @@ -4360,7 +4423,7 @@ struct Extension template const X& get_subtable () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.template get_subtable (); default:return Null (typename T::SubTable); } @@ -4372,7 +4435,7 @@ struct Extension template typename hb_subset_context_t::return_t dispatch (hb_subset_context_t *c, Ts&&... ds) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.subset (c); default: return c->default_return_value (); } @@ -4381,9 +4444,9 @@ struct Extension template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.dispatch (c, std::forward (ds)...)); default:return_trace (c->default_return_value ()); } @@ -4391,7 +4454,7 @@ struct Extension protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ExtensionFormat1 format1; } u; }; @@ -4428,22 +4491,14 @@ struct hb_ot_layout_lookup_accelerator_t for (auto& subtable : hb_iter (thiz->subtables, count)) thiz->digest.union_ (subtable.digest); + thiz->count = count; + #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - if (c_accelerate_subtables.cache_user_cost < 4) - c_accelerate_subtables.cache_user_idx = (unsigned) -1; - - thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx; - - if (thiz->cache_user_idx != (unsigned) -1) - { - thiz->cache = thiz->subtables[thiz->cache_user_idx].cache_func (nullptr, hb_ot_lookup_cache_op_t::CREATE); - if (!thiz->cache) - thiz->cache_user_idx = (unsigned) -1; - } + thiz->subtable_cache_user_idx = c_accelerate_subtables.subtable_cache_user_idx; for (unsigned i = 0; i < count; i++) - if (i != thiz->cache_user_idx) - thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func; + if (i != thiz->subtable_cache_user_idx) + thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func; #endif return thiz; @@ -4452,11 +4507,8 @@ struct hb_ot_layout_lookup_accelerator_t void fini () { #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - if (cache) - { - assert (cache_user_idx != (unsigned) -1); - subtables[cache_user_idx].cache_func (cache, hb_ot_lookup_cache_op_t::DESTROY); - } + for (unsigned i = 0; i < count; i++) + hb_free (subtables[i].external_cache); #endif } @@ -4466,14 +4518,14 @@ struct hb_ot_layout_lookup_accelerator_t #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE #endif - bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const + bool apply (hb_ot_apply_context_t *c, bool use_cache) const { c->lookup_accel = this; #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE if (use_cache) { return - + hb_iter (hb_iter (subtables, subtables_count)) + + hb_iter (hb_iter (subtables, count)) | hb_map ([&c] (const hb_accelerate_subtables_context_t::hb_applicable_t &_) { return _.apply_cached (c); }) | hb_any ; @@ -4482,7 +4534,7 @@ struct hb_ot_layout_lookup_accelerator_t #endif { return - + hb_iter (hb_iter (subtables, subtables_count)) + + hb_iter (hb_iter (subtables, count)) | hb_map ([&c] (const hb_accelerate_subtables_context_t::hb_applicable_t &_) { return _.apply (c); }) | hb_any ; @@ -4493,8 +4545,8 @@ struct hb_ot_layout_lookup_accelerator_t bool cache_enter (hb_ot_apply_context_t *c) const { #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - return cache_user_idx != (unsigned) -1 && - subtables[cache_user_idx].cache_enter (c); + return subtable_cache_user_idx != (unsigned) -1 && + subtables[subtable_cache_user_idx].cache_enter (c); #else return false; #endif @@ -4502,19 +4554,17 @@ struct hb_ot_layout_lookup_accelerator_t void cache_leave (hb_ot_apply_context_t *c) const { #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - subtables[cache_user_idx].cache_leave (c); + subtables[subtable_cache_user_idx].cache_leave (c); #endif } hb_set_digest_t digest; + private: + unsigned count = 0; /* Number of subtables in the array. */ #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE - public: - void *cache = nullptr; - private: - unsigned cache_user_idx = (unsigned) -1; + unsigned subtable_cache_user_idx = (unsigned) -1; #endif - private: hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY]; }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc index afe52963201..c73e4dc4b6d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc @@ -343,7 +343,7 @@ hb_ot_layout_get_glyphs_in_class (hb_face_t *face, * @face: The #hb_face_t to work on * @glyph: The #hb_codepoint_t code point to query * @start_offset: offset of the first attachment point to retrieve - * @point_count: (inout) (optional): Input = the maximum number of attachment points to return; + * @point_count: (inout) (nullable): Input = the maximum number of attachment points to return; * Output = the actual number of attachment points returned (may be zero) * @point_array: (out) (array length=point_count): The array of attachment points found for the query * @@ -373,7 +373,7 @@ hb_ot_layout_get_attach_points (hb_face_t *face, * @direction: The #hb_direction_t text direction to use * @glyph: The #hb_codepoint_t code point to query * @start_offset: offset of the first caret position to retrieve - * @caret_count: (inout) (optional): Input = the maximum number of caret positions to return; + * @caret_count: (inout) (nullable): Input = the maximum number of caret positions to return; * Output = the actual number of caret positions returned (may be zero) * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query * @@ -444,7 +444,7 @@ get_gsubgpos_table (hb_face_t *face, * @face: #hb_face_t to work upon * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @start_offset: offset of the first script tag to retrieve - * @script_count: (inout) (optional): Input = the maximum number of script tags to return; + * @script_count: (inout) (nullable): Input = the maximum number of script tags to return; * Output = the actual number of script tags returned (may be zero) * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query * @@ -541,8 +541,8 @@ hb_ot_layout_table_choose_script (hb_face_t *face, * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @script_count: Number of script tags in the array * @script_tags: Array of #hb_tag_t script tags - * @script_index: (out) (optional): The index of the requested script - * @chosen_script: (out) (optional): #hb_tag_t of the requested script + * @script_index: (out) (nullable): The index of the requested script + * @chosen_script: (out) (nullable): #hb_tag_t of the requested script * * Selects an OpenType script for @table_tag from the @script_tags array. * @@ -613,7 +613,7 @@ hb_ot_layout_table_select_script (hb_face_t *face, * @face: #hb_face_t to work upon * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @start_offset: offset of the first feature tag to retrieve - * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; * Output = the actual number of feature tags returned (may be zero) * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table * @@ -683,7 +683,7 @@ hb_ot_layout_table_find_feature (hb_face_t *face, * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @script_index: The index of the requested script tag * @start_offset: offset of the first language tag to retrieve - * @language_count: (inout) (optional): Input = the maximum number of language tags to return; + * @language_count: (inout) (nullable): Input = the maximum number of language tags to return; * Output = the actual number of language tags returned (may be zero) * @language_tags: (out) (array length=language_count): Array of language tags found in the table * @@ -911,7 +911,7 @@ hb_ot_layout_language_get_required_feature (hb_face_t *face, * @script_index: The index of the requested script tag * @language_index: The index of the requested language tag * @start_offset: offset of the first feature tag to retrieve - * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; * Output: the actual number of feature tags returned (may be zero) * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query * @@ -947,7 +947,7 @@ hb_ot_layout_language_get_feature_indexes (hb_face_t *face, * @script_index: The index of the requested script tag * @language_index: The index of the requested language tag * @start_offset: offset of the first feature tag to retrieve - * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return; * Output = the actual number of feature tags returned (may be zero) * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query * @@ -1035,7 +1035,7 @@ hb_ot_layout_language_find_feature (hb_face_t *face, * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @feature_index: The index of the requested feature * @start_offset: offset of the first lookup to retrieve - * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return; + * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return; * Output = the actual number of lookups returned (may be zero) * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query * @@ -1386,10 +1386,10 @@ hb_ot_layout_collect_lookups (hb_face_t *face, * @face: #hb_face_t to work upon * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS * @lookup_index: The index of the feature lookup to query - * @glyphs_before: (out): Array of glyphs preceding the substitution range - * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup - * @glyphs_after: (out): Array of glyphs following the substitution range - * @glyphs_output: (out): Array of glyphs that would be the substituted output of the lookup + * @glyphs_before: (out) (nullable): Array of glyphs preceding the substitution range + * @glyphs_input: (out) (nullable): Array of input glyphs that would be substituted by the lookup + * @glyphs_after: (out) (nullable): Array of glyphs following the substitution range + * @glyphs_output: (out) (nullable): Array of glyphs that would be the substituted output of the lookup * * Fetches a list of all glyphs affected by the specified lookup in the * specified face's GSUB table or GPOS table. @@ -1473,7 +1473,7 @@ hb_ot_layout_table_find_feature_variations (hb_face_t *face, * @feature_index: The index of the feature to query * @variations_index: The index of the feature variation to query * @start_offset: offset of the first lookup to retrieve - * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return; + * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return; * Output = the actual number of lookups returned (may be zero) * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query * @@ -1777,15 +1777,15 @@ hb_ot_layout_get_size_params (hb_face_t *face, * @face: #hb_face_t to work upon * @table_tag: table tag to query, "GSUB" or "GPOS". * @feature_index: index of feature to query. - * @label_id: (out) (optional): The ‘name’ table name ID that specifies a string - * for a user-interface label for this feature. (May be NULL.) - * @tooltip_id: (out) (optional): The ‘name’ table name ID that specifies a string + * @label_id: (out) (nullable): The ‘name’ table name ID that specifies a string + * for a user-interface label for this feature. + * @tooltip_id: (out) (nullable): The ‘name’ table name ID that specifies a string * that an application can use for tooltip text for this - * feature. (May be NULL.) - * @sample_id: (out) (optional): The ‘name’ table name ID that specifies sample text - * that illustrates the effect of this feature. (May be NULL.) - * @num_named_parameters: (out) (optional): Number of named parameters. (May be zero.) - * @first_param_id: (out) (optional): The first ‘name’ table name ID used to specify + * feature. + * @sample_id: (out) (nullable): The ‘name’ table name ID that specifies sample text + * that illustrates the effect of this feature. + * @num_named_parameters: (out) (nullable): Number of named parameters. + * @first_param_id: (out) (nullable): The first ‘name’ table name ID used to specify * strings for user-interface labels for the feature * parameters. (Must be zero if numParameters is zero.) * @@ -1852,7 +1852,7 @@ hb_ot_layout_feature_get_name_ids (hb_face_t *face, * @table_tag: table tag to query, "GSUB" or "GPOS". * @feature_index: index of feature to query. * @start_offset: offset of the first character to retrieve - * @char_count: (inout) (optional): Input = the maximum number of characters to return; + * @char_count: (inout) (nullable): Input = the maximum number of characters to return; * Output = the actual number of characters returned (may be zero) * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. * The Unicode codepoints of the characters for which this feature provides @@ -1915,31 +1915,33 @@ struct GPOSProxy static inline bool apply_forward (OT::hb_ot_apply_context_t *c, - const OT::hb_ot_layout_lookup_accelerator_t &accel, - unsigned subtable_count) + const OT::hb_ot_layout_lookup_accelerator_t &accel) { - bool use_cache = accel.cache_enter (c); + bool use_hot_subtable_cache = accel.cache_enter (c); bool ret = false; hb_buffer_t *buffer = c->buffer; - while (buffer->idx < buffer->len && buffer->successful) + while (buffer->successful) { - bool applied = false; - auto &cur = buffer->cur(); - if (accel.digest.may_have (cur.codepoint) && - (cur.mask & c->lookup_mask) && - c->check_glyph_property (&cur, c->lookup_props)) - { - applied = accel.apply (c, subtable_count, use_cache); - } + hb_glyph_info_t *info = buffer->info; + unsigned j = buffer->idx; + while (j < buffer->len && + !(accel.digest.may_have (info[j].codepoint) && + (info[j].mask & c->lookup_mask) && + c->check_glyph_property (&info[j], c->lookup_props))) + j++; + if (unlikely (j > buffer->idx && !buffer->next_glyphs (j - buffer->idx))) + break; + if (buffer->idx >= buffer->len) + break; - if (applied) + if (accel.apply (c, use_hot_subtable_cache)) ret = true; else (void) buffer->next_glyph (); } - if (use_cache) + if (use_hot_subtable_cache) accel.cache_leave (c); return ret; @@ -1947,8 +1949,7 @@ apply_forward (OT::hb_ot_apply_context_t *c, static inline bool apply_backward (OT::hb_ot_apply_context_t *c, - const OT::hb_ot_layout_lookup_accelerator_t &accel, - unsigned subtable_count) + const OT::hb_ot_layout_lookup_accelerator_t &accel) { bool ret = false; hb_buffer_t *buffer = c->buffer; @@ -1958,11 +1959,10 @@ apply_backward (OT::hb_ot_apply_context_t *c, if (accel.digest.may_have (cur.codepoint) && (cur.mask & c->lookup_mask) && c->check_glyph_property (&cur, c->lookup_props)) - ret |= accel.apply (c, subtable_count, false); + ret |= accel.apply (c, false); /* The reverse lookup doesn't "advance" cursor (for good reason). */ buffer->idx--; - } while ((int) buffer->idx >= 0); return ret; @@ -1975,7 +1975,6 @@ apply_string (OT::hb_ot_apply_context_t *c, const OT::hb_ot_layout_lookup_accelerator_t &accel) { hb_buffer_t *buffer = c->buffer; - unsigned subtable_count = lookup.get_subtable_count (); if (unlikely (!buffer->len || !c->lookup_mask)) return false; @@ -1991,7 +1990,7 @@ apply_string (OT::hb_ot_apply_context_t *c, buffer->clear_output (); buffer->idx = 0; - ret = apply_forward (c, accel, subtable_count); + ret = apply_forward (c, accel); if (!Proxy::always_inplace) buffer->sync (); @@ -2001,7 +2000,7 @@ apply_string (OT::hb_ot_apply_context_t *c, /* in-place backward substitution/positioning */ assert (!buffer->have_output); buffer->idx = buffer->len - 1; - ret = apply_backward (c, accel, subtable_count); + ret = apply_backward (c, accel); } return ret; @@ -2017,7 +2016,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, unsigned int i = 0; auto *font_data = font->data.ot.get (); - auto *var_store_cache = font_data == HB_SHAPER_DATA_SUCCEEDED ? nullptr : (OT::ItemVariationStore::cache_t *) font_data; + auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data; OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache); c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func); @@ -2037,11 +2036,8 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, if (buffer->messaging () && !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue; - /* c.digest is a digest of all the current glyphs in the buffer - * (plus some past glyphs). - * - * Only try applying the lookup if there is any overlap. */ - if (accel->digest.may_intersect (c.digest)) + /* Only try applying the lookup if there is any overlap. */ + if (accel->digest.may_intersect (buffer->digest)) { c.set_lookup_index (lookup_index); c.set_lookup_mask (lookup.mask, false); @@ -2067,7 +2063,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, if (stage->pause_func (plan, font, buffer)) { /* Refresh working buffer digest since buffer changed. */ - buffer->collect_codepoints (c.digest); + buffer->update_digest (); } } } @@ -2601,6 +2597,7 @@ hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font, #endif +#ifndef HB_NO_LAYOUT_RARELY_USED struct hb_get_glyph_alternates_dispatch_t : hb_dispatch_context_t { @@ -2620,14 +2617,13 @@ struct hb_get_glyph_alternates_dispatch_t : ( _dispatch (obj, hb_prioritize, std::forward (ds)...) ) }; -#ifndef HB_NO_LAYOUT_RARELY_USED /** * hb_ot_layout_lookup_get_glyph_alternates: * @face: a face. * @lookup_index: index of the feature lookup to query. * @glyph: a glyph id. * @start_offset: starting offset. - * @alternate_count: (inout) (optional): Input = the maximum number of alternate glyphs to return; + * @alternate_count: (inout) (nullable): Input = the maximum number of alternate glyphs to return; * Output = the actual number of alternate glyphs returned (may be zero). * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer. * Alternate glyphs associated with the glyph id. @@ -2654,6 +2650,64 @@ hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, return ret; } +struct hb_collect_glyph_alternates_dispatch_t : + hb_dispatch_context_t +{ + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return false; } + + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( (obj.collect_glyph_alternates (std::forward (ds)...), true) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( default_return_value () ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, std::forward (ds)...) ) +}; + +/** + * hb_ot_layout_lookup_collect_glyph_alternates: + * @face: a face. + * @lookup_index: index of the feature lookup to query. + * @alternate_count: (inout): mapping from glyph index to number of alternates for that glyph. + * @alternate_glyphs: (inout): mapping from encoded glyph index and alternate index, to alternate glyph ids. + * + * Collects alternates of glyphs from a given GSUB lookup index. + * + * For one-to-one GSUB glyph substitutions, this function collects the + * substituted glyph. + * + * For lookups that assign multiple alternates to a glyph, all alternate glyphs are collected. + * + * For other lookup types, nothing is performed and `false` is returned. + * + * The `alternate_count` mapping will contain the number of alternates for each glyph id. + * Upon entry, this mapping should contain the glyph ids as keys, and the number of alternates + * currently known for each glyph id as values. + * + * The `alternate_glyphs` mapping will contain the alternate glyph ids for each glyph id. + * The mapping is encoded in the following way, upon entry and after processing: + * If G is the glyph id, and A0, A1, ..., A(n-1) are the alternate glyph ids, + * the mapping will contain the following entries: (G + (i << 24)) -> A(i) + * for i = 0, 1, ..., n-1 where n is the number of alternates for G as per `alternate_count`. + * + * Return value: `true` if alternates were collected, `false` otherwise. + * Since: 12.1.0 + */ +HB_EXTERN hb_bool_t +hb_ot_layout_lookup_collect_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */) +{ + hb_collect_glyph_alternates_dispatch_t c; + const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index); + return lookup.dispatch (&c, alternate_count, alternate_glyphs); +} struct hb_position_single_dispatch_t : hb_dispatch_context_t diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h index 1d9db1105bd..b814c2acda3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h @@ -383,6 +383,12 @@ hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, unsigned *alternate_count /* IN/OUT */, hb_codepoint_t *alternate_glyphs /* OUT */); +HB_EXTERN hb_bool_t +hb_ot_layout_lookup_collect_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_map_t *alternate_count /* IN/OUT */, + hb_map_t *alternate_glyphs /* IN/OUT */); + HB_EXTERN hb_bool_t hb_ot_layout_lookup_would_substitute (hb_face_t *face, unsigned int lookup_index, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh index a68c8421e4c..f669ac7c46d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh @@ -217,8 +217,6 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) if (u >= 0x80u) { - buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII; - if (unlikely (unicode->is_default_ignorable (u))) { buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES; @@ -247,6 +245,7 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (gen_cat))) { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS; props |= UPROPS_MASK_CONTINUATION; props |= unicode->modified_combining_class (u)<<8; } @@ -361,8 +360,9 @@ _hb_glyph_info_unhide (hb_glyph_info_t *info) } static inline void -_hb_glyph_info_set_continuation (hb_glyph_info_t *info) +_hb_glyph_info_set_continuation (hb_glyph_info_t *info, hb_buffer_t *buffer) { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS; info->unicode_props() |= UPROPS_MASK_CONTINUATION; } static inline void @@ -410,18 +410,6 @@ _hb_glyph_info_is_zwj (const hb_glyph_info_t *info) return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWJ); } static inline bool -_hb_glyph_info_is_joiner (const hb_glyph_info_t *info) -{ - return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ)); -} -static inline void -_hb_glyph_info_flip_joiners (hb_glyph_info_t *info) -{ - if (!_hb_glyph_info_is_unicode_format (info)) - return; - info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ; -} -static inline bool _hb_glyph_info_is_aat_deleted (const hb_glyph_info_t *info) { return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_AAT_DELETED); @@ -657,4 +645,18 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer) #undef lig_props #undef glyph_props +static inline void +_hb_collect_glyph_alternates_add (hb_codepoint_t from, + hb_codepoint_t to, + hb_map_t *alternate_count, + hb_map_t *alternate_glyphs) +{ + hb_codepoint_t zero = 0; + hb_codepoint_t *i = &zero; + alternate_count->has (from, &i); + alternate_glyphs->set (from | (*i << 24), to); + alternate_count->set (from, *i + 1); +} + + #endif /* HB_OT_LAYOUT_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh index b33e01fd6c9..ad9d7cb03e8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh @@ -69,6 +69,8 @@ struct MathValueRecord struct MathConstants { + friend struct MATH; + MathConstants* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); @@ -1109,8 +1111,8 @@ struct MATH { #ifndef HB_NO_MATH switch HB_CODEPOINT_ENCODE3 (font->face->table.MATH.get_blob ()->length, - get_constant (HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT, font), - get_constant (HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT, font)) + (this+mathConstants).minHeight[1], // displayOperatorMinHeight + (this+mathConstants).minHeight[0]) // delimitedSubFormulaMinHeight { /* sha1sum:ab4a4fe054d23061f3c039493d6f665cfda2ecf5 cambria.ttc * sha1sum:086855301bff644f9d8827b88491fcf73a6d4cb9 cambria.ttc diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-macroman.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-macroman.hh index b4df8aaeeab..269b4d3fe45 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-macroman.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-macroman.hh @@ -31,264 +31,264 @@ #endif -_S(".notdef") -_S(".null") -_S("nonmarkingreturn") -_S("space") -_S("exclam") -_S("quotedbl") -_S("numbersign") -_S("dollar") -_S("percent") -_S("ampersand") -_S("quotesingle") -_S("parenleft") -_S("parenright") -_S("asterisk") -_S("plus") -_S("comma") -_S("hyphen") -_S("period") -_S("slash") -_S("zero") -_S("one") -_S("two") -_S("three") -_S("four") -_S("five") -_S("six") -_S("seven") -_S("eight") -_S("nine") -_S("colon") -_S("semicolon") -_S("less") -_S("equal") -_S("greater") -_S("question") -_S("at") -_S("A") -_S("B") -_S("C") -_S("D") -_S("E") -_S("F") -_S("G") -_S("H") -_S("I") -_S("J") -_S("K") -_S("L") -_S("M") -_S("N") -_S("O") -_S("P") -_S("Q") -_S("R") -_S("S") -_S("T") -_S("U") -_S("V") -_S("W") -_S("X") -_S("Y") -_S("Z") -_S("bracketleft") -_S("backslash") -_S("bracketright") -_S("asciicircum") -_S("underscore") -_S("grave") -_S("a") -_S("b") -_S("c") -_S("d") -_S("e") -_S("f") -_S("g") -_S("h") -_S("i") -_S("j") -_S("k") -_S("l") -_S("m") -_S("n") -_S("o") -_S("p") -_S("q") -_S("r") -_S("s") -_S("t") -_S("u") -_S("v") -_S("w") -_S("x") -_S("y") -_S("z") -_S("braceleft") -_S("bar") -_S("braceright") -_S("asciitilde") -_S("Adieresis") -_S("Aring") -_S("Ccedilla") -_S("Eacute") -_S("Ntilde") -_S("Odieresis") -_S("Udieresis") -_S("aacute") -_S("agrave") -_S("acircumflex") -_S("adieresis") -_S("atilde") -_S("aring") -_S("ccedilla") -_S("eacute") -_S("egrave") -_S("ecircumflex") -_S("edieresis") -_S("iacute") -_S("igrave") -_S("icircumflex") -_S("idieresis") -_S("ntilde") -_S("oacute") -_S("ograve") -_S("ocircumflex") -_S("odieresis") -_S("otilde") -_S("uacute") -_S("ugrave") -_S("ucircumflex") -_S("udieresis") -_S("dagger") -_S("degree") -_S("cent") -_S("sterling") -_S("section") -_S("bullet") -_S("paragraph") -_S("germandbls") -_S("registered") -_S("copyright") -_S("trademark") -_S("acute") -_S("dieresis") -_S("notequal") -_S("AE") -_S("Oslash") -_S("infinity") -_S("plusminus") -_S("lessequal") -_S("greaterequal") -_S("yen") -_S("mu") -_S("partialdiff") -_S("summation") -_S("product") -_S("pi") -_S("integral") -_S("ordfeminine") -_S("ordmasculine") -_S("Omega") -_S("ae") -_S("oslash") -_S("questiondown") -_S("exclamdown") -_S("logicalnot") -_S("radical") -_S("florin") -_S("approxequal") -_S("Delta") -_S("guillemotleft") -_S("guillemotright") -_S("ellipsis") -_S("nonbreakingspace") -_S("Agrave") -_S("Atilde") -_S("Otilde") -_S("OE") -_S("oe") -_S("endash") -_S("emdash") -_S("quotedblleft") -_S("quotedblright") -_S("quoteleft") -_S("quoteright") -_S("divide") -_S("lozenge") -_S("ydieresis") -_S("Ydieresis") -_S("fraction") -_S("currency") -_S("guilsinglleft") -_S("guilsinglright") -_S("fi") -_S("fl") -_S("daggerdbl") -_S("periodcentered") -_S("quotesinglbase") -_S("quotedblbase") -_S("perthousand") -_S("Acircumflex") -_S("Ecircumflex") -_S("Aacute") -_S("Edieresis") -_S("Egrave") -_S("Iacute") -_S("Icircumflex") -_S("Idieresis") -_S("Igrave") -_S("Oacute") -_S("Ocircumflex") -_S("apple") -_S("Ograve") -_S("Uacute") -_S("Ucircumflex") -_S("Ugrave") -_S("dotlessi") -_S("circumflex") -_S("tilde") -_S("macron") -_S("breve") -_S("dotaccent") -_S("ring") -_S("cedilla") -_S("hungarumlaut") -_S("ogonek") -_S("caron") -_S("Lslash") -_S("lslash") -_S("Scaron") -_S("scaron") -_S("Zcaron") -_S("zcaron") -_S("brokenbar") -_S("Eth") -_S("eth") -_S("Yacute") -_S("yacute") -_S("Thorn") -_S("thorn") -_S("minus") -_S("multiply") -_S("onesuperior") -_S("twosuperior") -_S("threesuperior") -_S("onehalf") -_S("onequarter") -_S("threequarters") -_S("franc") -_S("Gbreve") -_S("gbreve") -_S("Idotaccent") -_S("Scedilla") -_S("scedilla") -_S("Cacute") -_S("cacute") -_S("Ccaron") -_S("ccaron") -_S("dcroat") +HB_STR(".notdef") +HB_STR(".null") +HB_STR("nonmarkingreturn") +HB_STR("space") +HB_STR("exclam") +HB_STR("quotedbl") +HB_STR("numbersign") +HB_STR("dollar") +HB_STR("percent") +HB_STR("ampersand") +HB_STR("quotesingle") +HB_STR("parenleft") +HB_STR("parenright") +HB_STR("asterisk") +HB_STR("plus") +HB_STR("comma") +HB_STR("hyphen") +HB_STR("period") +HB_STR("slash") +HB_STR("zero") +HB_STR("one") +HB_STR("two") +HB_STR("three") +HB_STR("four") +HB_STR("five") +HB_STR("six") +HB_STR("seven") +HB_STR("eight") +HB_STR("nine") +HB_STR("colon") +HB_STR("semicolon") +HB_STR("less") +HB_STR("equal") +HB_STR("greater") +HB_STR("question") +HB_STR("at") +HB_STR("A") +HB_STR("B") +HB_STR("C") +HB_STR("D") +HB_STR("E") +HB_STR("F") +HB_STR("G") +HB_STR("H") +HB_STR("I") +HB_STR("J") +HB_STR("K") +HB_STR("L") +HB_STR("M") +HB_STR("N") +HB_STR("O") +HB_STR("P") +HB_STR("Q") +HB_STR("R") +HB_STR("S") +HB_STR("T") +HB_STR("U") +HB_STR("V") +HB_STR("W") +HB_STR("X") +HB_STR("Y") +HB_STR("Z") +HB_STR("bracketleft") +HB_STR("backslash") +HB_STR("bracketright") +HB_STR("asciicircum") +HB_STR("underscore") +HB_STR("grave") +HB_STR("a") +HB_STR("b") +HB_STR("c") +HB_STR("d") +HB_STR("e") +HB_STR("f") +HB_STR("g") +HB_STR("h") +HB_STR("i") +HB_STR("j") +HB_STR("k") +HB_STR("l") +HB_STR("m") +HB_STR("n") +HB_STR("o") +HB_STR("p") +HB_STR("q") +HB_STR("r") +HB_STR("s") +HB_STR("t") +HB_STR("u") +HB_STR("v") +HB_STR("w") +HB_STR("x") +HB_STR("y") +HB_STR("z") +HB_STR("braceleft") +HB_STR("bar") +HB_STR("braceright") +HB_STR("asciitilde") +HB_STR("Adieresis") +HB_STR("Aring") +HB_STR("Ccedilla") +HB_STR("Eacute") +HB_STR("Ntilde") +HB_STR("Odieresis") +HB_STR("Udieresis") +HB_STR("aacute") +HB_STR("agrave") +HB_STR("acircumflex") +HB_STR("adieresis") +HB_STR("atilde") +HB_STR("aring") +HB_STR("ccedilla") +HB_STR("eacute") +HB_STR("egrave") +HB_STR("ecircumflex") +HB_STR("edieresis") +HB_STR("iacute") +HB_STR("igrave") +HB_STR("icircumflex") +HB_STR("idieresis") +HB_STR("ntilde") +HB_STR("oacute") +HB_STR("ograve") +HB_STR("ocircumflex") +HB_STR("odieresis") +HB_STR("otilde") +HB_STR("uacute") +HB_STR("ugrave") +HB_STR("ucircumflex") +HB_STR("udieresis") +HB_STR("dagger") +HB_STR("degree") +HB_STR("cent") +HB_STR("sterling") +HB_STR("section") +HB_STR("bullet") +HB_STR("paragraph") +HB_STR("germandbls") +HB_STR("registered") +HB_STR("copyright") +HB_STR("trademark") +HB_STR("acute") +HB_STR("dieresis") +HB_STR("notequal") +HB_STR("AE") +HB_STR("Oslash") +HB_STR("infinity") +HB_STR("plusminus") +HB_STR("lessequal") +HB_STR("greaterequal") +HB_STR("yen") +HB_STR("mu") +HB_STR("partialdiff") +HB_STR("summation") +HB_STR("product") +HB_STR("pi") +HB_STR("integral") +HB_STR("ordfeminine") +HB_STR("ordmasculine") +HB_STR("Omega") +HB_STR("ae") +HB_STR("oslash") +HB_STR("questiondown") +HB_STR("exclamdown") +HB_STR("logicalnot") +HB_STR("radical") +HB_STR("florin") +HB_STR("approxequal") +HB_STR("Delta") +HB_STR("guillemotleft") +HB_STR("guillemotright") +HB_STR("ellipsis") +HB_STR("nonbreakingspace") +HB_STR("Agrave") +HB_STR("Atilde") +HB_STR("Otilde") +HB_STR("OE") +HB_STR("oe") +HB_STR("endash") +HB_STR("emdash") +HB_STR("quotedblleft") +HB_STR("quotedblright") +HB_STR("quoteleft") +HB_STR("quoteright") +HB_STR("divide") +HB_STR("lozenge") +HB_STR("ydieresis") +HB_STR("Ydieresis") +HB_STR("fraction") +HB_STR("currency") +HB_STR("guilsinglleft") +HB_STR("guilsinglright") +HB_STR("fi") +HB_STR("fl") +HB_STR("daggerdbl") +HB_STR("periodcentered") +HB_STR("quotesinglbase") +HB_STR("quotedblbase") +HB_STR("perthousand") +HB_STR("Acircumflex") +HB_STR("Ecircumflex") +HB_STR("Aacute") +HB_STR("Edieresis") +HB_STR("Egrave") +HB_STR("Iacute") +HB_STR("Icircumflex") +HB_STR("Idieresis") +HB_STR("Igrave") +HB_STR("Oacute") +HB_STR("Ocircumflex") +HB_STR("apple") +HB_STR("Ograve") +HB_STR("Uacute") +HB_STR("Ucircumflex") +HB_STR("Ugrave") +HB_STR("dotlessi") +HB_STR("circumflex") +HB_STR("tilde") +HB_STR("macron") +HB_STR("breve") +HB_STR("dotaccent") +HB_STR("ring") +HB_STR("cedilla") +HB_STR("hungarumlaut") +HB_STR("ogonek") +HB_STR("caron") +HB_STR("Lslash") +HB_STR("lslash") +HB_STR("Scaron") +HB_STR("scaron") +HB_STR("Zcaron") +HB_STR("zcaron") +HB_STR("brokenbar") +HB_STR("Eth") +HB_STR("eth") +HB_STR("Yacute") +HB_STR("yacute") +HB_STR("Thorn") +HB_STR("thorn") +HB_STR("minus") +HB_STR("multiply") +HB_STR("onesuperior") +HB_STR("twosuperior") +HB_STR("threesuperior") +HB_STR("onehalf") +HB_STR("onequarter") +HB_STR("threequarters") +HB_STR("franc") +HB_STR("Gbreve") +HB_STR("gbreve") +HB_STR("Idotaccent") +HB_STR("Scedilla") +HB_STR("scedilla") +HB_STR("Cacute") +HB_STR("cacute") +HB_STR("Ccaron") +HB_STR("ccaron") +HB_STR("dcroat") #endif /* HB_OT_POST_MACROMAN_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc index 2401e18f0e6..c17e7628975 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc @@ -409,16 +409,13 @@ position_around_base (const hb_ot_shape_plan_t *plan, } static inline void -position_cluster (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer, - unsigned int start, - unsigned int end, - bool adjust_offsets_when_zeroing) +position_cluster_impl (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool adjust_offsets_when_zeroing) { - if (end - start < 2) - return; - /* Find the base glyph */ hb_glyph_info_t *info = buffer->info; for (unsigned int i = start; i < end; i++) @@ -441,6 +438,20 @@ position_cluster (const hb_ot_shape_plan_t *plan, } } +static HB_ALWAYS_INLINE void +position_cluster (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool adjust_offsets_when_zeroing) +{ + if (end - start < 2) + return; + + position_cluster_impl (plan, font, buffer, start, end, adjust_offsets_when_zeroing); +} + void _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, hb_font_t *font, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc index cb941bc7fbd..508ed6a5076 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc @@ -78,14 +78,14 @@ static inline void set_glyph (hb_glyph_info_t &info, hb_font_t *font) { - (void) font->get_nominal_glyph (info.codepoint, &info.glyph_index()); + (void) font->get_nominal_glyph (info.codepoint, &info.normalizer_glyph_index()); } static inline void output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph) { /* This is very confusing indeed. */ - buffer->cur().glyph_index() = glyph; + buffer->cur().normalizer_glyph_index() = glyph; (void) buffer->output_glyph (unichar); _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer); } @@ -93,7 +93,7 @@ output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph) static inline void next_char (hb_buffer_t *buffer, hb_codepoint_t glyph) { - buffer->cur().glyph_index() = glyph; + buffer->cur().normalizer_glyph_index() = glyph; (void) buffer->next_glyph (); } @@ -210,7 +210,7 @@ handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, hb_font_t * const font = c->font; for (; buffer->idx < end - 1 && buffer->successful;) { if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) { - if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index())) + if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().normalizer_glyph_index())) { hb_codepoint_t unicode = buffer->cur().codepoint; (void) buffer->replace_glyphs (2, 1, &unicode); @@ -342,7 +342,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, unsigned int done = font->get_nominal_glyphs (end - buffer->idx, &buffer->cur().codepoint, sizeof (buffer->info[0]), - &buffer->cur().glyph_index(), + &buffer->cur().normalizer_glyph_index(), sizeof (buffer->info[0])); if (unlikely (!buffer->next_glyphs (done))) break; } @@ -456,7 +456,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, buffer->out_len--; /* Remove the second composable. */ /* Modify starter and carry on. */ buffer->out_info[starter].codepoint = composed; - buffer->out_info[starter].glyph_index() = glyph; + buffer->out_info[starter].normalizer_glyph_index() = glyph; _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); continue; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh index 9f17bdbb243..138e895e8e1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.hh @@ -32,7 +32,7 @@ /* buffer var allocations, used during the normalization process */ -#define glyph_index() var1.u32 +#define normalizer_glyph_index() var1.u32 struct hb_ot_shape_plan_t; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc index 477a7c7fc72..69b188f7aa3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc @@ -44,6 +44,7 @@ #include "hb-ot-face.hh" #include "hb-set.hh" +#include "hb-unicode.hh" #include "hb-aat-layout.hh" #include "hb-ot-layout-gdef-table.hh" @@ -92,7 +93,7 @@ hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *fac shaper = hb_ot_shaper_categorize (props.script, props.direction, map.chosen_script[0]); script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; - script_fallback_mark_positioning = shaper->fallback_position; + script_fallback_position = shaper->fallback_position; #ifndef HB_NO_AAT_SHAPE /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ @@ -178,12 +179,12 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, #endif #ifndef HB_NO_OT_KERN else if (hb_ot_layout_has_kerning (face)) - plan.apply_kern = true; + plan.apply_kern = script_fallback_position; // Not all shapers apply legacy `kern` #endif else {} } - plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); + plan.apply_fallback_kern = script_fallback_position && !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); plan.zero_marks = script_zero_marks && !plan.apply_kerx && @@ -203,7 +204,7 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, ); plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && - script_fallback_mark_positioning; + script_fallback_position; #ifndef HB_NO_AAT_SHAPE /* If we're using morx shaping, we cancel mark position adjustment because @@ -425,25 +426,20 @@ _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) */ struct hb_ot_font_data_t { - OT::ItemVariationStore::cache_t unused; // Just for alignment + OT::hb_scalar_cache_t unused; // Just for alignment }; hb_ot_font_data_t * _hb_ot_shaper_font_data_create (hb_font_t *font) { - if (!font->num_coords) - return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; - const OT::ItemVariationStore &var_store = font->face->table.GDEF->table->get_var_store (); - auto *cache = (hb_ot_font_data_t *) var_store.create_cache (); - return cache ? cache : (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + return (hb_ot_font_data_t *) var_store.create_cache (); } void _hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data) { - if (data == HB_SHAPER_DATA_SUCCEEDED) return; - OT::ItemVariationStore::destroy_cache ((OT::ItemVariationStore::cache_t *) data); + OT::ItemVariationStore::destroy_cache ((OT::hb_scalar_cache_t *) data); } @@ -488,6 +484,9 @@ hb_set_unicode_props (hb_buffer_t *buffer) { _hb_glyph_info_set_unicode_props (&info[i], buffer); + if (info[i].codepoint < 0x80) + continue; + unsigned gen_cat = _hb_glyph_info_get_general_category (&info[i]); if (FLAG_UNSAFE (gen_cat) & (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | @@ -502,7 +501,7 @@ hb_set_unicode_props (hb_buffer_t *buffer) if (unlikely (gen_cat == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL && hb_in_range (info[i].codepoint, 0x1F3FBu, 0x1F3FFu))) { - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); } /* Regional_Indicators are hairy as hell... * https://github.com/harfbuzz/harfbuzz/issues/2265 */ @@ -510,18 +509,18 @@ hb_set_unicode_props (hb_buffer_t *buffer) { if (_hb_codepoint_is_regional_indicator (info[i - 1].codepoint) && !_hb_glyph_info_is_continuation (&info[i - 1])) - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); } #ifndef HB_NO_EMOJI_SEQUENCES else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) { - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); if (i + 1 < count && _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint)) { i++; _hb_glyph_info_set_unicode_props (&info[i], buffer); - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); } } #endif @@ -540,7 +539,9 @@ hb_set_unicode_props (hb_buffer_t *buffer) * https://github.com/harfbuzz/harfbuzz/issues/3844 */ else if (unlikely (hb_in_ranges (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu))) - _hb_glyph_info_set_continuation (&info[i]); + _hb_glyph_info_set_continuation (&info[i], buffer); + else if (unlikely (info[i].codepoint == 0x2044u /* FRACTION SLASH */)) + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH; } } @@ -576,7 +577,7 @@ hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) static void hb_form_clusters (hb_buffer_t *buffer) { - if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS)) return; if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level)) @@ -618,14 +619,14 @@ hb_ensure_native_direction (hb_buffer_t *buffer) for (unsigned i = 0; i < count; i++) { auto gc = _hb_glyph_info_get_general_category (&info[i]); - if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) - found_number = true; - else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) + if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) { found_letter = true; break; } - else if (_hb_codepoint_is_regional_indicator (info[i].codepoint)) + else if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + found_number = true; + else if (unlikely (_hb_codepoint_is_regional_indicator (info[i].codepoint))) found_ri = true; } if ((found_number || found_ri) && !found_letter) @@ -651,59 +652,6 @@ hb_ensure_native_direction (hb_buffer_t *buffer) * Substitute */ -#ifndef HB_NO_VERTICAL -static hb_codepoint_t -hb_vert_char_for (hb_codepoint_t u) -{ - switch (u >> 8) - { - case 0x20: switch (u) { - case 0x2013u: return 0xfe32u; // EN DASH - case 0x2014u: return 0xfe31u; // EM DASH - case 0x2025u: return 0xfe30u; // TWO DOT LEADER - case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS - } break; - case 0x30: switch (u) { - case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA - case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP - case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET - case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET - case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET - case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET - case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET - case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET - case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET - case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET - case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET - case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET - case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET - case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET - case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET - case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET - } break; - case 0xfe: switch (u) { - case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE - } break; - case 0xff: switch (u) { - case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK - case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS - case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS - case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA - case 0xff1au: return 0xfe13u; // FULLWIDTH COLON - case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON - case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK - case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET - case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET - case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE - case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET - case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET - } break; - } - - return u; -} -#endif - static inline void hb_ot_rotate_chars (const hb_ot_shape_context_t *c) { @@ -729,7 +677,7 @@ hb_ot_rotate_chars (const hb_ot_shape_context_t *c) if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert) { for (unsigned int i = 0; i < count; i++) { - hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint); + hb_codepoint_t codepoint = hb_unicode_funcs_t::vertical_char_for (info[i].codepoint); if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) info[i].codepoint = codepoint; } @@ -744,7 +692,7 @@ hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) return; #endif - if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || + if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_FRACTION_SLASH) || !c->plan->has_frac) return; @@ -845,7 +793,13 @@ hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) unsigned int i = 0; for (i = 0; i < count; i++) if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) - pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; + { + pos[i].x_advance = pos[i].y_advance = 0; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + pos[i].x_offset = 0; + else + pos[i].y_offset = 0; + } } static void @@ -900,11 +854,11 @@ hb_ot_hide_default_ignorables (hb_buffer_t *buffer, static inline void hb_ot_map_glyphs_fast (hb_buffer_t *buffer) { - /* Normalization process sets up glyph_index(), we just copy it. */ + /* Normalization process sets up normalizer_glyph_index(), we just copy it. */ unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - info[i].codepoint = info[i].glyph_index(); + info[i].codepoint = info[i].normalizer_glyph_index(); buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; } @@ -942,7 +896,7 @@ hb_ot_substitute_default (const hb_ot_shape_context_t *c) hb_ot_rotate_chars (c); - HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); + HB_BUFFER_ALLOCATE_VAR (buffer, normalizer_glyph_index); _hb_ot_shape_normalize (c->plan, buffer, c->font); @@ -954,7 +908,7 @@ hb_ot_substitute_default (const hb_ot_shape_context_t *c) hb_ot_map_glyphs_fast (buffer); - HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); + HB_BUFFER_DEALLOCATE_VAR (buffer, normalizer_glyph_index); } static inline void @@ -969,11 +923,17 @@ hb_ot_substitute_plan (const hb_ot_shape_context_t *c) #ifndef HB_NO_AAT_SHAPE if (unlikely (c->plan->apply_morx)) + { hb_aat_layout_substitute (c->plan, c->font, c->buffer, c->user_features, c->num_user_features); + c->buffer->update_digest (); + } else #endif + { + c->buffer->update_digest (); c->plan->substitute (c->font, buffer); + } } static inline void @@ -1054,23 +1014,16 @@ hb_ot_position_default (const hb_ot_shape_context_t *c) { c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]), &pos[0].x_advance, sizeof(pos[0])); - /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ - if (c->font->has_glyph_h_origin_func ()) - for (unsigned int i = 0; i < count; i++) - c->font->subtract_glyph_h_origin (info[i].codepoint, - &pos[i].x_offset, - &pos[i].y_offset); + // h_origin defaults to zero; only apply it if the font has it. + if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) + c->font->subtract_glyph_h_origins (c->buffer); } else { c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]), &pos[0].y_advance, sizeof(pos[0])); - for (unsigned int i = 0; i < count; i++) - { - c->font->subtract_glyph_v_origin (info[i].codepoint, - &pos[i].x_offset, - &pos[i].y_offset); - } + // v_origin defaults to non-zero; apply even if only fallback is there. + c->font->subtract_glyph_v_origins (c->buffer); } if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer); @@ -1079,10 +1032,6 @@ hb_ot_position_default (const hb_ot_shape_context_t *c) static inline void hb_ot_position_plan (const hb_ot_shape_context_t *c) { - unsigned int count = c->buffer->len; - hb_glyph_info_t *info = c->buffer->info; - hb_glyph_position_t *pos = c->buffer->pos; - /* If the font has no GPOS and direction is forward, then when * zeroing mark widths, we shift the mark with it, such that the * mark is positioned hanging over the previous glyph. When @@ -1097,12 +1046,9 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c) /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ - /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ - if (c->font->has_glyph_h_origin_func ()) - for (unsigned int i = 0; i < count; i++) - c->font->add_glyph_h_origin (info[i].codepoint, - &pos[i].x_offset, - &pos[i].y_offset); + // h_origin defaults to zero; only apply it if the font has it. + if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) + c->font->add_glyph_h_origins (c->buffer); hb_ot_layout_position_start (c->font, c->buffer); @@ -1139,12 +1085,9 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c) hb_ot_zero_width_default_ignorables (c->buffer); hb_ot_layout_position_finish_offsets (c->font, c->buffer); - /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ - if (c->font->has_glyph_h_origin_func ()) - for (unsigned int i = 0; i < count; i++) - c->font->subtract_glyph_h_origin (info[i].codepoint, - &pos[i].x_offset, - &pos[i].y_offset); + // h_origin defaults to zero; only apply it if the font has it. + if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ()) + c->font->subtract_glyph_h_origins (c->buffer); if (c->plan->fallback_mark_positioning) _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer, @@ -1172,8 +1115,33 @@ hb_propagate_flags (hb_buffer_t *buffer) /* Propagate cluster-level glyph flags to be the same on all cluster glyphs. * Simplifies using them. */ - if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS)) + hb_mask_t and_mask = HB_GLYPH_FLAG_DEFINED; + if ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0) + and_mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + + hb_glyph_info_t *info = buffer->info; + + if ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0) + { + foreach_cluster (buffer, start, end) + { + if (end - start == 1) + { + info[start].mask &= and_mask; + continue; + } + + unsigned int mask = 0; + for (unsigned int i = start; i < end; i++) + mask |= info[i].mask; + + mask &= and_mask; + + for (unsigned int i = start; i < end; i++) + info[i].mask = mask; + } return; + } /* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things: * @@ -1181,30 +1149,20 @@ hb_propagate_flags (hb_buffer_t *buffer) * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_TATWEEL, * - Any place that is SAFE_TO_INSERT_TATWEEL, is also now UNSAFE_TO_BREAK. * - * We couldn't make this interaction earlier. It has to be done here. + * We couldn't make this interaction earlier. It has to be done this way. */ - bool flip_tatweel = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL; - - bool clear_concat = (buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0; - - hb_glyph_info_t *info = buffer->info; - foreach_cluster (buffer, start, end) { unsigned int mask = 0; for (unsigned int i = start; i < end; i++) - mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; + mask |= info[i].mask; - if (flip_tatweel) - { - if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) - mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; - if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) - mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; - } + if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) + mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; + if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) + mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; - if (clear_concat) - mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + mask &= and_mask; for (unsigned int i = start; i < end; i++) info[i].mask = mask; @@ -1245,8 +1203,6 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c) _hb_buffer_deallocate_unicode_vars (c->buffer); c->buffer->props.direction = c->target_direction; - - c->buffer->leave (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh index 068d7192d0d..649acddb414 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh @@ -150,7 +150,7 @@ struct hb_ot_shape_planner_t static constexpr bool apply_morx = false; #endif bool script_zero_marks : 1; - bool script_fallback_mark_positioning : 1; + bool script_fallback_position : 1; const struct hb_ot_shaper_t *shaper; HB_INTERNAL hb_ot_shape_planner_t (hb_face_t *face, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh index e38686e3ebb..79d3014d3e1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-joining-list.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-16.0.0.txt - * # Date: 2024-07-30 - * # Scripts-16.0.0.txt - * # Date: 2024-04-30, 21:48:40 GMT + * # ArabicShaping-17.0.0.txt + * # Date: 2025-08-14 + * # Scripts-17.0.0.txt + * # Date: 2025-07-24, 13:28:55 GMT */ #ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh index 19bd72d4218..5f87995ea9c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic-table.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-16.0.0.txt - * # Date: 2024-07-30 - * # Blocks-16.0.0.txt - * # Date: 2024-02-02 + * # ArabicShaping-17.0.0.txt + * # Date: 2025-08-14 + * # Blocks-17.0.0.txt + * # Date: 2025-08-01 * UnicodeData.txt does not have a header. */ @@ -80,7 +80,7 @@ static const uint8_t joining_table[] = /* Arabic Extended-B */ /* 0860 */ R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R, - /* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,X,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,D,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X, /* Arabic Extended-A */ @@ -140,9 +140,9 @@ static const uint8_t joining_table[] = /* Arabic Extended-C */ - /* 10EC0 */ R,D,D, + /* 10EC0 */ R,D,D,X,D,D, -#define joining_offset_0x10f30u 1185 +#define joining_offset_0x10f30u 1188 /* Sogdian */ @@ -161,14 +161,14 @@ static const uint8_t joining_table[] = /* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D, /* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L, -#define joining_offset_0x110bdu 1341 +#define joining_offset_0x110bdu 1344 /* Kaithi */ /* 110A0 */ U,X,X, /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U, -#define joining_offset_0x1e900u 1358 +#define joining_offset_0x1e900u 1361 /* Adlam */ @@ -176,7 +176,7 @@ static const uint8_t joining_table[] = /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, -}; /* Table items: 1434; occupancy: 57% */ +}; /* Table items: 1437; occupancy: 58% */ static unsigned int @@ -204,7 +204,7 @@ joining_type (hb_codepoint_t u) if (hb_in_range (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u]; if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; if (hb_in_range (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u]; - if (hb_in_range (u, 0x10EC2u, 0x10EC4u)) return joining_table[u - 0x10EC2u + joining_offset_0x10ec2u]; + if (hb_in_range (u, 0x10EC2u, 0x10EC7u)) return joining_table[u - 0x10EC2u + joining_offset_0x10ec2u]; if (hb_in_range (u, 0x10F30u, 0x10FCBu)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u]; break; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc index 7379bb7f15b..981c0f9224b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-arabic.cc @@ -654,7 +654,7 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, /* https://www.unicode.org/reports/tr53/ */ -static hb_codepoint_t +static const hb_codepoint_t modifier_combining_marks[] = { 0x0654u, /* ARABIC HAMZA ABOVE */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc index 5c46ebbc281..5f15aff8b7c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-hangul.cc @@ -427,7 +427,7 @@ const hb_ot_shaper_t _hb_ot_shaper_hangul = HB_TAG_NONE, /* gpos_tag */ HB_OT_SHAPE_NORMALIZATION_MODE_NONE, HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, - false, /* fallback_position */ + true, /* fallback_position */ }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh index 25e6d85ef80..76c5e83e30f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-machine.hh @@ -53,7 +53,7 @@ enum indic_syllable_type_t { }; -#line 57 "hb-ot-shaper-indic-machine.hh" +#line 54 "hb-ot-shaper-indic-machine.hh" #define indic_syllable_machine_ex_A 9u #define indic_syllable_machine_ex_C 1u #define indic_syllable_machine_ex_CM 16u @@ -77,7 +77,7 @@ enum indic_syllable_type_t { #define indic_syllable_machine_ex_ZWNJ 5u -#line 81 "hb-ot-shaper-indic-machine.hh" +#line 76 "hb-ot-shaper-indic-machine.hh" static const unsigned char _indic_syllable_machine_trans_keys[] = { 8u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 8u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 4u, 57u, @@ -1126,7 +1126,7 @@ find_syllables_indic (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 1130 "hb-ot-shaper-indic-machine.hh" +#line 1119 "hb-ot-shaper-indic-machine.hh" { cs = indic_syllable_machine_start; ts = 0; @@ -1142,7 +1142,7 @@ find_syllables_indic (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 1146 "hb-ot-shaper-indic-machine.hh" +#line 1131 "hb-ot-shaper-indic-machine.hh" { int _slen; int _trans; @@ -1156,7 +1156,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 1160 "hb-ot-shaper-indic-machine.hh" +#line 1143 "hb-ot-shaper-indic-machine.hh" } _keys = _indic_syllable_machine_trans_keys + (cs<<1); @@ -1268,7 +1268,7 @@ _eof_trans: #line 117 "hb-ot-shaper-indic-machine.rl" {act = 7;} break; -#line 1272 "hb-ot-shaper-indic-machine.hh" +#line 1232 "hb-ot-shaper-indic-machine.hh" } _again: @@ -1277,7 +1277,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1281 "hb-ot-shaper-indic-machine.hh" +#line 1239 "hb-ot-shaper-indic-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc index b87c530853b..bf27efee21b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic-table.cc @@ -6,12 +6,12 @@ * * on files with these headers: * - * # IndicSyllabicCategory-16.0.0.txt - * # Date: 2024-04-30, 21:48:21 GMT - * # IndicPositionalCategory-16.0.0.txt - * # Date: 2024-04-30, 21:48:21 GMT - * # Blocks-16.0.0.txt - * # Date: 2024-02-02 + * # IndicSyllabicCategory-17.0.0.txt + * # Date: 2025-08-01, 04:02:23 GMT + * # IndicPositionalCategory-17.0.0.txt + * # Date: 2025-07-29, 13:35:52 GMT + * # Blocks-17.0.0.txt + * # Date: 2025-08-01 */ #include "hb.hh" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc index d78b5670f61..bec802429ab 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-indic.cc @@ -296,11 +296,6 @@ struct indic_shape_plan_t const indic_config_t *config; bool is_old_spec; -#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE - bool uniscribe_bug_compatible; -#else - static constexpr bool uniscribe_bug_compatible = false; -#endif mutable hb_atomic_t virama_glyph; hb_indic_would_substitute_feature_t rphf; @@ -327,9 +322,6 @@ data_create_indic (const hb_ot_shape_plan_t *plan) } indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2'); -#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE - indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible; -#endif indic_plan->virama_glyph = -1; /* Use zero-context would_substitute() matching for new-spec of the main @@ -943,17 +935,7 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, unsigned int start, unsigned int end) { /* We treat placeholder/dotted-circle as if they are consonants, so we - * should just chain. Only if not in compatibility mode that is... */ - - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; - if (indic_plan->uniscribe_bug_compatible) - { - /* For dotted-circle, this is what Uniscribe does: - * If dotted-circle is the last glyph, it just does nothing. - * Ie. It doesn't form Reph. */ - if (buffer->info[end - 1].indic_category() == I_Cat(DOTTEDCIRCLE)) - return; - } + * should just chain... */ initial_reordering_consonant_syllable (plan, face, buffer, start, end); } @@ -1347,8 +1329,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, * Uniscribe doesn't do this. * TEST: U+0930,U+094D,U+0915,U+094B,U+094D */ - if (!indic_plan->uniscribe_bug_compatible && - unlikely (is_halant (info[new_reph_pos]))) + if (unlikely (is_halant (info[new_reph_pos]))) { for (unsigned int i = base + 1; i < new_reph_pos; i++) if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) @@ -1451,27 +1432,6 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, else buffer->unsafe_to_break (start - 1, start + 1); } - - - /* - * Finish off the clusters and go home! - */ - if (indic_plan->uniscribe_bug_compatible) - { - switch ((hb_tag_t) plan->props.script) - { - case HB_SCRIPT_TAMIL: - break; - - default: - /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil. - * This means, half forms are submerged into the main consonant's cluster. - * This is unnecessary, and makes cursor positioning harder, but that's what - * Uniscribe does. */ - buffer->merge_clusters (start, end); - break; - } - } } @@ -1501,9 +1461,7 @@ preprocess_text_indic (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, hb_font_t *font) { - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; - if (!indic_plan->uniscribe_bug_compatible) - _hb_preprocess_text_vowel_constraints (plan, buffer, font); + _hb_preprocess_text_vowel_constraints (plan, buffer, font); } static bool diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer-machine.hh index e1f657c758a..2442f0341ef 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer-machine.hh @@ -48,7 +48,7 @@ enum khmer_syllable_type_t { }; -#line 52 "hb-ot-shaper-khmer-machine.hh" +#line 49 "hb-ot-shaper-khmer-machine.hh" #define khmer_syllable_machine_ex_C 1u #define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u #define khmer_syllable_machine_ex_H 4u @@ -66,7 +66,7 @@ enum khmer_syllable_type_t { #define khmer_syllable_machine_ex_ZWNJ 5u -#line 70 "hb-ot-shaper-khmer-machine.hh" +#line 65 "hb-ot-shaper-khmer-machine.hh" static const unsigned char _khmer_syllable_machine_trans_keys[] = { 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, @@ -294,7 +294,7 @@ find_syllables_khmer (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 298 "hb-ot-shaper-khmer-machine.hh" +#line 287 "hb-ot-shaper-khmer-machine.hh" { cs = khmer_syllable_machine_start; ts = 0; @@ -310,7 +310,7 @@ find_syllables_khmer (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 314 "hb-ot-shaper-khmer-machine.hh" +#line 299 "hb-ot-shaper-khmer-machine.hh" { int _slen; int _trans; @@ -324,7 +324,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 328 "hb-ot-shaper-khmer-machine.hh" +#line 311 "hb-ot-shaper-khmer-machine.hh" } _keys = _khmer_syllable_machine_trans_keys + (cs<<1); @@ -394,7 +394,7 @@ _eof_trans: #line 98 "hb-ot-shaper-khmer-machine.rl" {act = 3;} break; -#line 398 "hb-ot-shaper-khmer-machine.hh" +#line 368 "hb-ot-shaper-khmer-machine.hh" } _again: @@ -403,7 +403,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 407 "hb-ot-shaper-khmer-machine.hh" +#line 375 "hb-ot-shaper-khmer-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer.cc index 2a4aed2ab1b..1f5028aa3ee 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-khmer.cc @@ -141,12 +141,6 @@ override_features_khmer (hb_ot_shape_planner_t *plan) * typographical correctness.", hence in overrides... */ map->enable_feature (HB_TAG('c','l','i','g')); - /* Uniscribe does not apply 'kern' in Khmer. */ - if (hb_options ().uniscribe_bug_compatible) - { - map->disable_feature (HB_TAG('k','e','r','n')); - } - map->disable_feature (HB_TAG('l','i','g','a')); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh index 64eb761b4ea..6e5fce609bc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-myanmar-machine.hh @@ -50,7 +50,7 @@ enum myanmar_syllable_type_t { }; -#line 54 "hb-ot-shaper-myanmar-machine.hh" +#line 51 "hb-ot-shaper-myanmar-machine.hh" #define myanmar_syllable_machine_ex_A 9u #define myanmar_syllable_machine_ex_As 32u #define myanmar_syllable_machine_ex_C 1u @@ -78,7 +78,7 @@ enum myanmar_syllable_type_t { #define myanmar_syllable_machine_ex_ZWNJ 5u -#line 82 "hb-ot-shaper-myanmar-machine.hh" +#line 77 "hb-ot-shaper-myanmar-machine.hh" static const unsigned char _myanmar_syllable_machine_trans_keys[] = { 1u, 57u, 3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u, @@ -549,7 +549,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 553 "hb-ot-shaper-myanmar-machine.hh" +#line 542 "hb-ot-shaper-myanmar-machine.hh" { cs = myanmar_syllable_machine_start; ts = 0; @@ -565,7 +565,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 569 "hb-ot-shaper-myanmar-machine.hh" +#line 554 "hb-ot-shaper-myanmar-machine.hh" { int _slen; int _trans; @@ -579,7 +579,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 583 "hb-ot-shaper-myanmar-machine.hh" +#line 566 "hb-ot-shaper-myanmar-machine.hh" } _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); @@ -649,7 +649,7 @@ _eof_trans: #line 113 "hb-ot-shaper-myanmar-machine.rl" {act = 3;} break; -#line 653 "hb-ot-shaper-myanmar-machine.hh" +#line 623 "hb-ot-shaper-myanmar-machine.hh" } _again: @@ -658,7 +658,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 662 "hb-ot-shaper-myanmar-machine.hh" +#line 630 "hb-ot-shaper-myanmar-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc index a0ea464e775..42fc4bbcc44 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-thai.cc @@ -163,7 +163,7 @@ thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font) } -static enum thai_above_state_t +static const enum thai_above_state_t { /* Cluster above looks like: */ T0, /* ⣤ */ T1, /* ⣼ */ @@ -191,7 +191,7 @@ static const struct thai_above_state_machine_edge_t { }; -static enum thai_below_state_t +static const enum thai_below_state_t { B0, /* No descender */ B1, /* Removable descender */ @@ -334,7 +334,7 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan, /* Is SARA AM. Decompose and reorder. */ (void) buffer->output_glyph (NIKHAHIT_FROM_SARA_AM (u)); - _hb_glyph_info_set_continuation (&buffer->prev()); + _hb_glyph_info_set_continuation (&buffer->prev(), buffer); if (unlikely (!buffer->replace_glyph (SARA_AA_FROM_SARA_AM (u)))) break; /* Make Nikhahit be recognized as a ccc=0 mark when zeroing widths. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh index 46f66f7d285..22a5356a877 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-machine.hh @@ -53,7 +53,7 @@ enum use_syllable_type_t { }; -#line 57 "hb-ot-shaper-use-machine.hh" +#line 54 "hb-ot-shaper-use-machine.hh" #define use_syllable_machine_ex_B 1u #define use_syllable_machine_ex_CGJ 6u #define use_syllable_machine_ex_CMAbv 31u @@ -100,7 +100,7 @@ enum use_syllable_type_t { #define use_syllable_machine_ex_ZWNJ 14u -#line 104 "hb-ot-shaper-use-machine.hh" +#line 99 "hb-ot-shaper-use-machine.hh" static const unsigned char _use_syllable_machine_trans_keys[] = { 49u, 51u, 0u, 56u, 11u, 56u, 11u, 56u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, @@ -929,7 +929,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int act HB_UNUSED; int cs; -#line 933 "hb-ot-shaper-use-machine.hh" +#line 922 "hb-ot-shaper-use-machine.hh" { cs = use_syllable_machine_start; ts = 0; @@ -942,7 +942,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 946 "hb-ot-shaper-use-machine.hh" +#line 931 "hb-ot-shaper-use-machine.hh" { int _slen; int _trans; @@ -956,7 +956,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 960 "hb-ot-shaper-use-machine.hh" +#line 943 "hb-ot-shaper-use-machine.hh" } _keys = _use_syllable_machine_trans_keys + (cs<<1); @@ -1078,7 +1078,7 @@ _eof_trans: #line 181 "hb-ot-shaper-use-machine.rl" {act = 9;} break; -#line 1082 "hb-ot-shaper-use-machine.hh" +#line 1039 "hb-ot-shaper-use-machine.hh" } _again: @@ -1087,7 +1087,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1091 "hb-ot-shaper-use-machine.hh" +#line 1046 "hb-ot-shaper-use-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh index d3c49949aa8..a69abefdd01 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-use-table.hh @@ -6,18 +6,18 @@ * * on files with these headers: * - * # IndicSyllabicCategory-16.0.0.txt - * # Date: 2024-04-30, 21:48:21 GMT - * # IndicPositionalCategory-16.0.0.txt - * # Date: 2024-04-30, 21:48:21 GMT - * # ArabicShaping-16.0.0.txt - * # Date: 2024-07-30 - * # DerivedCoreProperties-16.0.0.txt - * # Date: 2024-05-31, 18:09:32 GMT - * # Blocks-16.0.0.txt - * # Date: 2024-02-02 - * # Scripts-16.0.0.txt - * # Date: 2024-04-30, 21:48:40 GMT + * # IndicSyllabicCategory-17.0.0.txt + * # Date: 2025-08-01, 04:02:23 GMT + * # IndicPositionalCategory-17.0.0.txt + * # Date: 2025-07-29, 13:35:52 GMT + * # ArabicShaping-17.0.0.txt + * # Date: 2025-08-14 + * # DerivedCoreProperties-17.0.0.txt + * # Date: 2025-07-30, 23:55:08 GMT + * # Blocks-17.0.0.txt + * # Date: 2025-08-01 + * # Scripts-17.0.0.txt + * # Date: 2025-07-24, 13:28:55 GMT * # Override values For Indic_Syllabic_Category * # Not derivable * # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 @@ -101,8 +101,9 @@ #ifndef HB_OPTIMIZE_SIZE -static const uint8_t -hb_use_u8[3345] = +#include + +static const uint8_t hb_use_u8[3343]= { 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, @@ -126,24 +127,24 @@ hb_use_u8[3345] = 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 50, 51, 2, 2, 2, 2, 2, 2, 2, 2, 52, 53, 2, 54, 2, 2, 55, 56, 2, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 2, 70, 71, 72, 73, - 2, 74, 2, 75, 76, 77, 78, 2, 2, 79, 80, 81, 82, 2, 83, 84, - 2, 85, 85, 85, 85, 85, 85, 85, 85, 86, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 87, 2, 2, 2, 2, 2, 2, 2, + 2, 74, 2, 75, 76, 77, 78, 79, 2, 80, 81, 82, 83, 2, 84, 85, + 2, 86, 86, 86, 86, 86, 86, 86, 86, 87, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 88, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 88, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 89, 90, 2, 2, 2, 91, 2, 2, 2, 92, - 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 94, 94, 94, 95, 2, 2, 2, 2, 2, + 2, 2, 2, 89, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 90, 91, 2, 2, 2, 92, 2, 2, 2, 93, + 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 95, 95, 95, 96, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 96, 97, 2, 2, 2, 2, 2, - 2, 2, 2, 98, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 97, 98, 2, 2, 2, 2, 2, + 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 99, 2, 2, 100, 2, 2, 2, 101, 2, 102, 2, 2, 2, - 2, 2, 2, 103, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 104, 104, 105, 106, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 2, 2, 2, 100, 2, 2, 101, 2, 2, 2, 102, 2, 103, 2, 2, 2, + 2, 2, 2, 104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 105, 105, 106, 107, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -192,99 +193,99 @@ hb_use_u8[3345] = 48, 48, 48, 48, 15, 82, 83, 84, 85, 86, 87, 0, 0, 0, 0, 88, 0, 9, 0, 0, 30, 0, 89, 81, 90, 2, 2, 2, 2, 9, 0, 0, 0, 42, 42, 91, 92, 2, 2, 2, 2, 2, 2, 2, 2, 13, 9, 0, - 0, 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 9, 22, 80, 45, 22, 94, 61, 0, 0, 95, 96, 95, 95, 97, 98, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 9, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 29, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 0, 0, 2, 2, 2, 52, 99, 45, 0, - 0, 2, 2, 100, 101, 102, 103, 61, 63, 104, 16, 45, 22, 59, 21, 80, - 48, 48, 76, 11, 11, 11, 105, 46, 40, 11, 106, 74, 2, 2, 2, 2, - 2, 2, 2, 107, 22, 20, 20, 22, 48, 48, 22, 108, 2, 2, 2, 9, - 0, 0, 0, 0, 0, 0, 109, 110, 110, 110, 110, 0, 0, 0, 0, 0, - 0, 106, 74, 2, 2, 2, 2, 2, 2, 60, 61, 59, 25, 22, 111, 61, - 2, 2, 2, 2, 107, 22, 23, 45, 45, 102, 112, 0, 0, 0, 0, 0, - 0, 2, 2, 61, 18, 48, 23, 113, 102, 102, 102, 114, 115, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 0, 30, 2, 11, 46, 116, 116, 116, 11, 116, - 116, 15, 116, 116, 116, 26, 0, 40, 0, 0, 0, 117, 51, 11, 5, 0, - 0, 0, 0, 0, 0, 0, 118, 0, 0, 0, 0, 0, 0, 0, 6, 119, - 120, 42, 42, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 120, - 121, 120, 120, 120, 120, 120, 120, 120, 120, 0, 0, 122, 0, 0, 0, 0, - 0, 0, 7, 122, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 123, 123, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, - 124, 0, 123, 123, 0, 0, 0, 0, 0, 2, 53, 2, 108, 2, 10, 2, - 2, 2, 65, 19, 16, 0, 0, 31, 0, 2, 2, 0, 0, 0, 0, 0, - 0, 29, 2, 2, 2, 2, 2, 2, 2, 2, 2, 125, 23, 23, 23, 23, - 23, 23, 23, 126, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 2, 0, 0, 0, 0, 0, 52, 2, 2, 2, 22, 22, 127, 116, - 0, 2, 2, 2, 128, 20, 59, 20, 113, 102, 129, 0, 0, 0, 0, 0, - 0, 11, 130, 2, 2, 2, 2, 2, 2, 2, 131, 23, 22, 20, 48, 132, - 133, 134, 0, 0, 0, 0, 0, 0, 0, 2, 2, 52, 30, 2, 2, 2, - 2, 2, 2, 2, 2, 10, 22, 59, 99, 76, 135, 136, 137, 0, 0, 0, - 0, 2, 138, 2, 2, 2, 2, 139, 0, 30, 2, 42, 5, 0, 79, 15, - 2, 53, 22, 140, 52, 53, 2, 2, 105, 10, 9, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 141, 21, 25, 0, 0, 142, 143, 0, 0, 0, - 0, 2, 65, 45, 23, 80, 47, 144, 0, 81, 81, 81, 81, 81, 81, 81, - 81, 0, 0, 0, 0, 0, 0, 0, 6, 120, 120, 120, 120, 121, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 2, 30, 2, 2, 2, - 2, 2, 30, 2, 2, 2, 30, 9, 0, 128, 20, 27, 31, 0, 0, 145, - 146, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, 2, 0, 14, 37, 0, - 147, 2, 2, 13, 37, 0, 30, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 30, 2, 2, 9, 2, 2, 11, 41, 0, 0, 0, - 0, 2, 2, 2, 0, 27, 22, 22, 30, 2, 2, 2, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 27, 38, 0, 2, 2, 2, 116, 116, 116, 116, - 116, 148, 2, 9, 0, 0, 0, 0, 0, 2, 14, 14, 0, 0, 0, 0, - 0, 9, 2, 2, 9, 2, 2, 2, 2, 30, 2, 9, 0, 30, 2, 0, - 0, 149, 150, 151, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 22, 20, - 20, 20, 22, 22, 134, 0, 0, 0, 0, 0, 152, 152, 152, 152, 152, 152, - 152, 152, 152, 152, 2, 2, 2, 2, 2, 53, 52, 53, 0, 0, 0, 0, - 153, 11, 74, 2, 2, 2, 2, 2, 2, 18, 19, 21, 16, 24, 37, 0, - 0, 0, 31, 0, 0, 0, 0, 0, 0, 11, 49, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 128, 20, 22, 154, 22, 21, 155, 156, 2, 2, 2, 2, - 2, 0, 0, 65, 157, 0, 0, 0, 0, 2, 13, 0, 0, 0, 0, 0, - 0, 2, 65, 25, 20, 20, 20, 22, 22, 108, 158, 0, 0, 56, 159, 31, - 160, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, - 19, 22, 22, 161, 44, 0, 0, 0, 49, 128, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 9, 9, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, - 30, 2, 2, 2, 2, 2, 2, 2, 10, 18, 19, 21, 22, 162, 31, 0, - 0, 11, 11, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 58, 17, - 23, 16, 23, 47, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 0, - 2, 2, 23, 0, 11, 11, 11, 46, 0, 11, 11, 46, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 30, 0, 9, 2, 2, 2, 30, 45, 59, 20, - 20, 31, 33, 32, 32, 25, 163, 29, 164, 165, 37, 0, 0, 0, 0, 0, - 0, 12, 26, 0, 0, 0, 0, 0, 0, 2, 2, 65, 25, 20, 20, 20, - 22, 23, 126, 15, 17, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, - 166, 167, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 20, 66, 99, 25, - 160, 11, 168, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 65, 25, 20, 20, 0, 48, 48, 11, 169, 37, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 2, 20, 0, 23, 19, 20, 20, 21, 16, 82, - 169, 38, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 10, 170, - 25, 20, 22, 22, 168, 9, 0, 0, 0, 2, 2, 2, 2, 2, 9, 43, - 136, 23, 22, 20, 76, 21, 22, 0, 0, 2, 2, 2, 9, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 18, 19, 20, 21, 22, 105, 169, 37, 0, - 0, 2, 2, 2, 9, 30, 0, 2, 2, 2, 2, 30, 9, 2, 2, 2, - 2, 23, 23, 18, 32, 33, 12, 171, 165, 172, 173, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 2, 2, 2, 65, 25, 20, 20, 0, 22, 23, - 29, 108, 0, 33, 0, 0, 0, 0, 0, 52, 20, 22, 22, 22, 140, 2, - 2, 2, 174, 175, 11, 15, 176, 61, 177, 0, 0, 1, 147, 0, 0, 0, - 0, 52, 20, 22, 16, 19, 20, 2, 2, 2, 2, 158, 158, 158, 178, 178, - 178, 178, 178, 178, 15, 179, 0, 30, 0, 22, 20, 20, 31, 22, 22, 11, + 0, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 93, 61, 0, + 0, 94, 95, 94, 94, 96, 97, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, + 0, 2, 2, 2, 2, 29, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, + 0, 2, 2, 2, 52, 98, 45, 0, 0, 2, 2, 99, 100, 101, 102, 61, + 63, 103, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 104, 46, + 40, 11, 105, 74, 2, 2, 2, 2, 2, 2, 2, 106, 22, 20, 20, 22, + 48, 48, 22, 107, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 108, 109, + 109, 109, 109, 0, 0, 0, 0, 0, 0, 105, 74, 2, 2, 2, 2, 2, + 2, 60, 61, 59, 25, 22, 110, 61, 2, 2, 2, 2, 106, 22, 23, 45, + 45, 101, 111, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 112, + 101, 101, 101, 113, 114, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, + 2, 11, 46, 115, 115, 115, 11, 115, 115, 15, 115, 115, 115, 26, 0, 40, + 0, 0, 0, 116, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 117, 0, + 0, 0, 0, 0, 0, 0, 6, 118, 119, 42, 42, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 119, 120, 119, 119, 119, 119, 119, 119, 119, + 119, 0, 0, 121, 0, 0, 0, 0, 0, 0, 7, 121, 0, 0, 0, 0, + 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 0, 0, 0, 0, 122, 122, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 30, 0, 0, 0, 0, 0, 0, 0, 123, 0, 122, 122, 0, 0, 0, 0, + 0, 2, 53, 2, 107, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 29, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 124, 23, 23, 23, 23, 23, 23, 23, 125, 0, 0, 0, 0, + 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 0, 0, 0, 0, 0, + 52, 2, 2, 2, 22, 22, 126, 115, 0, 2, 2, 2, 127, 20, 59, 20, + 112, 101, 128, 0, 0, 0, 0, 0, 0, 11, 129, 2, 2, 2, 2, 2, + 2, 2, 130, 23, 22, 20, 48, 131, 132, 133, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 52, 30, 2, 2, 2, 2, 2, 2, 2, 2, 10, 22, 59, + 98, 76, 134, 135, 136, 0, 0, 0, 0, 2, 137, 2, 2, 2, 2, 138, + 0, 30, 2, 42, 5, 0, 79, 15, 2, 139, 20, 53, 127, 139, 2, 2, + 140, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, + 25, 0, 0, 142, 143, 0, 0, 0, 0, 2, 65, 45, 23, 80, 47, 144, + 0, 81, 81, 81, 81, 81, 81, 81, 81, 0, 0, 0, 0, 0, 0, 0, + 6, 119, 119, 119, 119, 120, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, + 2, 2, 9, 2, 30, 2, 2, 2, 2, 2, 30, 2, 2, 2, 30, 9, + 0, 127, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, + 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, + 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 0, 27, 22, 22, + 30, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, + 0, 2, 2, 2, 115, 115, 115, 115, 115, 148, 2, 9, 0, 0, 0, 0, + 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, + 2, 30, 2, 9, 0, 30, 2, 0, 0, 149, 150, 151, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 133, 0, 0, 0, + 0, 0, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 2, 2, 2, 2, + 2, 53, 52, 53, 0, 0, 0, 0, 153, 11, 74, 2, 2, 2, 2, 2, + 2, 18, 19, 21, 16, 24, 37, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 127, 20, 22, 154, + 22, 21, 155, 156, 2, 2, 2, 2, 2, 0, 0, 65, 157, 0, 0, 0, + 0, 2, 13, 0, 0, 0, 0, 0, 0, 2, 65, 25, 20, 20, 20, 22, + 22, 107, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 23, 19, 22, 22, 161, 44, 0, 0, 0, + 49, 127, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, + 30, 2, 2, 2, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, + 10, 18, 19, 21, 22, 162, 31, 0, 0, 11, 11, 30, 2, 2, 2, 9, + 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, + 0, 0, 0, 0, 35, 0, 0, 0, 2, 2, 23, 0, 11, 11, 11, 46, + 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 30, 0, + 9, 2, 2, 2, 30, 45, 59, 20, 20, 31, 33, 32, 32, 25, 163, 29, + 164, 165, 37, 0, 0, 0, 0, 0, 0, 12, 26, 0, 0, 0, 0, 0, + 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 125, 15, 17, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 0, 166, 167, 0, 0, 0, 0, 0, 0, + 0, 18, 19, 20, 20, 66, 98, 25, 160, 11, 168, 9, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, + 169, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, + 0, 23, 19, 20, 20, 21, 16, 82, 169, 38, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 10, 170, 25, 20, 22, 22, 168, 9, 0, 0, + 0, 2, 2, 2, 2, 2, 9, 43, 135, 23, 22, 20, 76, 21, 22, 0, + 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, + 19, 20, 21, 22, 104, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, + 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 171, + 165, 172, 173, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, + 2, 65, 25, 20, 20, 0, 22, 23, 29, 107, 0, 33, 0, 0, 0, 0, + 0, 52, 20, 22, 22, 22, 139, 2, 2, 2, 174, 140, 11, 15, 175, 61, + 176, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, + 2, 2, 2, 158, 158, 158, 177, 177, 177, 177, 177, 177, 15, 178, 0, 30, + 0, 16, 20, 16, 16, 0, 0, 0, 0, 22, 20, 20, 31, 22, 22, 11, 169, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, - 27, 11, 159, 180, 181, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, + 27, 11, 159, 179, 180, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, - 0, 2, 182, 66, 47, 0, 0, 0, 0, 11, 183, 2, 2, 2, 2, 2, + 0, 2, 181, 66, 47, 0, 0, 0, 0, 11, 182, 2, 2, 2, 2, 2, 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 156, 0, 0, 184, 184, 184, 184, 184, 184, 184, - 184, 185, 185, 185, 186, 187, 185, 184, 184, 188, 184, 184, 189, 190, 190, 190, - 190, 190, 190, 190, 0, 0, 0, 0, 0, 184, 184, 184, 184, 184, 191, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 192, 193, - 194, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, + 0, 2, 2, 2, 2, 2, 156, 0, 0, 183, 183, 183, 183, 183, 183, 183, + 183, 184, 184, 184, 185, 186, 184, 183, 183, 187, 183, 183, 188, 189, 189, 189, + 189, 189, 189, 189, 0, 0, 0, 0, 0, 183, 183, 183, 183, 183, 190, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 191, 192, + 193, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 65, 47, 0, 2, 2, 2, 2, 2, 9, 0, - 58, 195, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 0, 0, 0, 40, 116, 26, 0, 0, 0, 0, 0, + 58, 194, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 0, 0, 0, 40, 115, 26, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 120, 120, 120, 121, 0, + 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 119, 119, 119, 120, 0, 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, @@ -300,23 +301,21 @@ hb_use_u8[3345] = VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv, FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB, - CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, - VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, - VPst, H, B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv, - CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, - SE, O, H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B, - CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, - FPst, VBlw, B, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, + CMAbv,CMAbv, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, + MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, + B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw, + VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, + H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, + MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, + B, VBlw,VMAbv, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, - VPst, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, - IS, VBlw, IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, - J, HR, G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, - VBlw, + VPst, MPst, R, MPst,CMBlw, B,FMBlw, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, + IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, J, HR, + G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, }; -static const uint16_t -hb_use_u16[856] = +static const uint16_t hb_use_u16[864]= { 0, 0, 1, 2, 0, 3, 0, 3, 0, 0, 4, 5, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, @@ -330,66 +329,65 @@ hb_use_u16[856] = 73, 74, 75, 76, 77, 0, 0, 0, 10, 10, 78, 79, 80, 81, 82, 83, 84, 85, 0, 0, 0, 0, 0, 0, 10, 86, 10, 87, 10, 88, 89, 90, 10, 10, 10, 91, 92, 93, 2, 0, 94, 0, 10, 10, 10, 10, 10, 95, - 96, 10, 97, 0, 0, 0, 0, 0, 98, 99,100,101, 31, 10,102,103, - 10, 10,104, 10,105,106, 0, 0, 10,107, 10, 10, 10,108,109,110, - 2, 2, 0, 0, 0, 0, 0, 0,111, 10, 10,112,113, 2,114,115, - 116, 10,117, 10, 10, 10,118,119, 10, 10,120,121,122, 0, 0, 0, - 0, 0, 0, 0, 0,123,124,125, 0, 0, 0, 0, 0, 0, 0,126, - 127,128,129, 0, 0, 0,130,131,132, 0, 0, 0, 0, 0, 0,133, - 0, 0, 0, 0,134, 0, 0, 0, 0, 0, 0, 0, 0, 0,135, 0, - 0, 0, 0, 10, 10, 10,136,137, 0, 0,138, 0, 0, 0, 0, 0, - 139, 10,140, 0, 10, 10, 10,141,142, 10, 10,143,144, 2,145,146, - 10, 10,147, 10,148,149, 0, 0,150, 10, 10,151,152, 2,153, 99, - 10, 10,154,155,156, 2, 10,157, 10, 10, 10,158,159, 0,160,161, - 0, 0, 0, 0, 10, 10,162, 2,163, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0,165, - 0, 0, 0, 0, 0, 0, 0,166,166,167, 34,168, 0, 0, 0, 0, - 169,170, 10,171, 95, 0, 0, 0, 0, 0, 0, 0, 70, 10,172, 0, - 10,173,174, 0, 0, 0, 0, 0, 10, 10,175, 2, 9, 10,176, 10, - 177, 0, 0, 0, 0, 0, 0, 0, 10, 10,178,173, 0, 0, 0, 0, - 0, 0, 0, 10,179,180, 0, 10,181, 0, 0,182,183, 0, 0, 0, - 184, 10, 10,185,186,187,188,189,190, 10, 10,191,192, 0, 0, 0, - 193, 10,194,195,196, 10, 10,197,190, 10, 10,198,199,106,200,103, - 10, 34,201,202,203, 0, 0, 0,204,205, 95, 10, 10,206,207, 2, - 208, 21, 22,209,210,211,212,213,214, 10, 10,215,216,217,218, 0, - 10, 10, 10,219,220,221,222, 0,200, 10, 10,223,224, 2, 0, 0, - 10, 10,225,226,227,228, 0, 0, 10, 10, 10,229,230, 2, 0, 0, - 10, 10,231,232, 2, 10,141, 0, 10,233,234,104,235, 0, 0, 0, - 10, 10,236,237, 0, 0, 0, 0,238,239, 10,240,241, 2, 0, 0, - 0, 0,242, 10, 10,243,244, 0,245, 10, 10,246,247,248, 10, 10, - 249,250, 0, 0, 0, 0, 0, 0, 22, 10,225,251, 8, 10, 71, 19, - 10,252, 74,253, 0, 0, 0, 0,254, 10, 10,255,256, 2,257, 10, - 258,259, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,260, - 261, 49, 10,262,263,264, 0, 0,265,265,265,265,265,265,265,265, - 265,265,265,266,267,268,265,265,265,265,265,265,265,265,265,269, - 10,270,271, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, - 10, 10, 10,272, 0, 0, 0, 0, 0, 0, 0, 0,273, 10,274, 2, - 10, 10, 10, 10,275,276,277,277,278,279, 0, 0, 0, 0,280, 0, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,177, 0,281, - 10, 10, 10, 10, 10, 10,106, 71, 95,282, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,283, 10, 10, 71,284,285, 0, 0, 0, - 0, 10,286, 0, 10, 10,287, 2, 0, 0, 0, 0, 0, 10,288, 2, - 0, 0, 0, 0, 0, 10,289,106, 10, 10, 10, 10,290, 2, 0, 0, - 130,130,130,130,130,130,130,130,163,163,163,163,163,163,163,163, - 163,163,163,163,163,163,163,130, + 96, 10, 97, 0, 0, 0, 0, 0, 10, 98, 99,100, 31, 10,101,102, + 10, 10,103, 10,104,105, 0, 0, 10,106, 10, 10, 10,107,108,109, + 2, 2, 0, 0, 0, 0, 0, 0,110, 10, 10,111,112, 2,113,114, + 115, 10,116, 10, 10, 10,117,118, 10, 10,119,120,121, 0, 0, 0, + 0, 0, 0, 0, 0,122,123,124, 0, 0, 0, 0, 0, 0, 0,125, + 126,127,128, 0, 0, 0,129,130,131, 0, 0, 0, 0, 0, 0,132, + 0, 0, 0, 0,133, 0, 0, 0, 0, 0, 0, 0, 0, 0,134, 0, + 0, 0, 0, 10, 10, 10,135,136, 0, 0,137, 0, 0, 0, 0, 0, + 138, 10,139, 0, 10, 10, 10,140,141, 10, 10,142,143, 2,144,145, + 10, 10,146, 10,147,148, 0, 0,149, 10, 10,150,151, 2,152, 98, + 10, 10,153,154,155, 2, 10,156, 10, 10, 10,157,158, 0,159,160, + 0, 0, 0, 0, 10, 10,161, 2,162, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 0, 0, 0, 0, 0,164, + 0, 0, 0, 0, 0, 0, 0,165,165,166, 34,167, 0, 0, 0, 0, + 168,169, 10,170, 95, 0, 0, 0, 0, 0, 0, 0, 70, 10,171, 0, + 10,172,173, 0, 0, 0, 0, 0, 10, 10,174, 2, 9, 10,175, 10, + 176, 0, 0, 0, 0, 0, 0, 0, 10, 10,177,172, 0, 0, 0, 0, + 0, 0, 0, 10,178,179, 0, 10,180, 0, 0,181,182, 0, 0, 0, + 183, 10, 10,184,185,186,187,188,189, 10, 10,190,191, 0, 0, 0, + 192, 10,193,194,195, 10, 10,196,189, 10, 10,197,198,105,199,102, + 10, 34,200,201,202, 0, 0, 0,203,204, 95, 10, 10,205,206, 2, + 207, 21, 22,208,209,210,211,212,213, 10, 10,214,215,216,217, 0, + 10, 10, 10,218,219,220,221, 0,199, 10, 10,222,223, 2, 0, 0, + 10, 10,224,225,226,227, 0, 0, 10, 10, 10,228,229, 2, 0, 0, + 10, 10,230,231, 2, 10,140, 0, 10,232,233,103,234, 0, 0, 0, + 10, 10,235,236, 0, 0, 0, 0,237,238, 10,239,240, 2, 0, 0, + 0, 0,241, 10, 10,242,243, 0,244, 10, 10,245,246,247, 10, 10, + 248,249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,250, 0, + 22, 10,224,251, 8, 10, 71, 19, 10,252, 74,253, 0, 0, 0, 0, + 254, 10, 10,255,256, 2,257, 10,258,259, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 10,260,261, 49, 10,262,263,264, 0, 0, + 265,265,265,265,265,265,265,265,265,265,265,266,267,268,265,265, + 265,265,265,265,265,265,265,269, 10,270,271, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10,272, 0, 0, 0, 0, + 0, 0, 0, 0,273, 10,274, 2, 10, 10, 10, 10,275,276,277,277, + 278,279, 0, 0, 0, 0,280, 0, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10,176, 0,281, 10, 10, 10, 10, 10, 10,105, 71, + 95,282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,283, + 10, 10, 71,284,285, 0, 0, 0, 0, 10,286, 0, 10, 10,287, 2, + 0, 0, 0, 0, 0, 10,288, 2, 0, 0, 0, 0, 0, 10,289,105, + 10, 10, 10, 10,290, 2, 0, 0,129,129,129,129,129,129,129,129, + 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,129, }; -static inline unsigned -hb_use_b4 (const uint8_t* a, unsigned i) +static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline uint_fast8_t -hb_use_get_category (unsigned u) +static inline uint8_t hb_use_get_category (unsigned u) { - return u<921600u?hb_use_u8[2953+(((hb_use_u8[625+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; + return u<921600 ? hb_use_u8[2953u+((hb_use_u8[625u+((hb_use_u16[((hb_use_u8[113u+((hb_use_b4(hb_use_u8,((((((((u)>>1))>>3))>>3))>>5)))<<5)+((((((((u)>>1))>>3))>>3))&31)])<<3)+((((((u)>>1))>>3))&7)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : O; } #else -static const uint8_t -hb_use_u8[3657] = +#include + +static const uint8_t hb_use_u8[3663]= { 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, @@ -405,16 +403,16 @@ hb_use_u8[3657] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 25, 26, 27, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, 30, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 32, 33, 1, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 1, 48, 49, 50, - 51, 52, 52, 52, 52, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 54, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 55, 1, 1, 1, 1, 1, 1, 1, 1, 56, 57, 1, 58, 1, - 59, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 60, 61, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 62, 1, 1, - 1, 1, 63, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 64, 65, 1, 66, 67, 1, 1, 1, 68, 1, 1, 1, 1, 1, - 1, 69, 70, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 0, 1, 2, 2, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 53, 53, 53, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, 1, 1, 1, 57, 58, 1, 59, 1, + 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 61, 62, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 63, 1, 1, + 1, 1, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 65, 66, 1, 67, 68, 1, 1, 1, 69, 1, 1, 1, 1, 1, + 1, 70, 71, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 0, 1, 2, 2, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 9, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, @@ -438,120 +436,120 @@ hb_use_u8[3657] = 0, 0, 0, 0, 0, 56, 179, 180, 0, 56, 181, 182, 0, 56, 183, 184, 185, 186, 187, 188, 0, 0, 0, 0, 0, 56, 189, 0, 0, 0, 0, 0, 0, 190, 191, 192, 0, 0, 193, 194, 195, 196, 197, 198, 56, 199, 0, 0, - 0, 200, 201, 202, 203, 204, 205, 0, 0, 206, 207, 208, 209, 210, 67, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 211, 212, 213, 214, 0, 0, 0, 0, - 0, 215, 215, 215, 215, 215, 215, 215, 215, 215, 216, 217, 215, 215, 215, 215, - 215, 215, 215, 215, 215, 215, 215, 215, 218, 219, 220, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 67, 0, 56, 221, 0, 0, 0, 0, 0, - 0, 0, 0, 222, 223, 0, 0, 0, 0, 56, 56, 224, 225, 226, 0, 0, - 227, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 228, - 229, 56, 56, 56, 230, 231, 0, 0, 0, 0, 0, 0, 232, 0, 0, 0, - 0, 56, 233, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 235, 56, - 236, 0, 0, 0, 0, 0, 0, 101, 237, 0, 0, 0, 0, 0, 0, 101, - 238, 56, 56, 239, 0, 0, 0, 0, 0, 240, 240, 240, 240, 240, 240, 240, - 240, 241, 241, 241, 241, 241, 241, 241, 242, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 6, - 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 10, 11, 11, 11, 11, 0, 0, 0, 9, 12, - 0, 2, 2, 2, 2, 13, 14, 0, 0, 11, 15, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 21, 22, 16, 23, 24, - 25, 12, 26, 27, 20, 2, 2, 2, 2, 2, 20, 0, 2, 2, 2, 2, - 2, 0, 2, 2, 2, 2, 2, 2, 2, 28, 29, 30, 2, 2, 2, 9, - 30, 9, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, - 2, 9, 9, 0, 2, 2, 0, 17, 18, 19, 20, 31, 32, 33, 32, 34, - 0, 0, 0, 0, 35, 0, 0, 2, 30, 2, 0, 0, 0, 0, 0, 9, - 36, 12, 15, 30, 2, 2, 9, 0, 30, 9, 2, 30, 9, 2, 0, 37, - 18, 19, 31, 0, 27, 38, 27, 39, 0, 40, 0, 0, 0, 30, 2, 9, - 9, 0, 0, 0, 2, 2, 2, 2, 2, 41, 42, 43, 0, 0, 0, 0, - 0, 12, 15, 30, 2, 2, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, - 2, 9, 2, 30, 2, 2, 0, 17, 18, 19, 20, 21, 27, 22, 35, 24, - 0, 0, 0, 0, 0, 30, 41, 41, 44, 12, 29, 30, 2, 2, 2, 9, - 30, 9, 2, 30, 2, 2, 0, 17, 45, 0, 0, 27, 22, 0, 0, 2, - 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 46, 30, 2, 2, 9, 0, - 2, 9, 2, 2, 0, 30, 9, 9, 2, 0, 30, 9, 0, 2, 9, 0, - 2, 2, 2, 2, 2, 2, 0, 0, 23, 16, 47, 0, 48, 33, 48, 34, - 0, 0, 0, 0, 35, 0, 0, 0, 0, 15, 29, 49, 2, 2, 2, 9, - 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 17, - 22, 16, 23, 47, 22, 38, 22, 39, 0, 0, 0, 27, 31, 2, 9, 0, - 0, 10, 29, 30, 2, 2, 2, 9, 2, 2, 2, 30, 2, 2, 0, 17, - 45, 0, 0, 35, 47, 0, 0, 0, 9, 50, 51, 0, 0, 0, 0, 0, - 0, 11, 29, 2, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 52, 53, - 23, 19, 20, 31, 48, 33, 48, 34, 54, 0, 0, 0, 35, 0, 0, 0, - 30, 12, 29, 30, 2, 2, 2, 2, 2, 2, 2, 2, 9, 0, 2, 2, - 2, 2, 30, 2, 2, 2, 2, 30, 0, 2, 2, 2, 9, 0, 55, 0, - 35, 23, 22, 31, 31, 18, 48, 48, 25, 0, 23, 0, 0, 0, 0, 0, - 0, 2, 0, 2, 9, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, - 0, 2, 2, 56, 56, 57, 0, 0, 18, 2, 2, 2, 2, 30, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 9, 0, 58, 21, 59, 22, 22, 20, 20, - 46, 21, 11, 31, 11, 2, 2, 60, 61, 61, 61, 61, 61, 62, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 63, - 0, 0, 0, 0, 64, 0, 0, 0, 0, 2, 2, 2, 2, 2, 65, 45, - 59, 66, 22, 22, 67, 68, 69, 70, 71, 2, 2, 2, 2, 2, 1, 0, - 5, 2, 2, 2, 23, 20, 2, 2, 72, 71, 73, 74, 65, 73, 29, 29, - 2, 52, 22, 53, 2, 2, 2, 2, 2, 2, 75, 76, 77, 29, 29, 78, - 79, 2, 2, 2, 2, 2, 29, 45, 0, 2, 59, 80, 0, 0, 0, 0, - 30, 2, 59, 47, 0, 0, 0, 0, 0, 2, 59, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 9, 2, 9, 59, 0, 0, 0, 0, 0, - 0, 2, 2, 81, 45, 22, 59, 20, 48, 48, 48, 48, 15, 82, 83, 84, - 85, 86, 87, 0, 0, 0, 0, 88, 0, 9, 0, 0, 30, 0, 89, 81, - 90, 2, 2, 2, 2, 9, 0, 0, 0, 42, 42, 91, 92, 2, 2, 2, - 2, 2, 2, 2, 2, 13, 9, 0, 0, 93, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 94, 61, 0, - 0, 95, 96, 95, 95, 97, 98, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 200, 0, 0, 0, 0, 201, 202, 203, 204, 205, 206, 0, + 0, 207, 208, 209, 210, 211, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 212, 213, 214, 215, 0, 0, 0, 0, 0, 216, 216, 216, 216, 216, 216, 216, + 216, 216, 217, 218, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, + 219, 220, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, + 0, 56, 222, 0, 0, 0, 0, 0, 0, 0, 0, 223, 224, 0, 0, 0, + 0, 56, 56, 225, 226, 227, 0, 0, 228, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 229, 230, 56, 56, 56, 231, 232, 0, 0, + 0, 0, 0, 0, 233, 0, 0, 0, 0, 56, 234, 235, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 236, 56, 237, 0, 0, 0, 0, 0, 0, 101, + 238, 0, 0, 0, 0, 0, 0, 101, 239, 56, 56, 240, 0, 0, 0, 0, + 0, 241, 241, 241, 241, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, + 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, + 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 11, + 11, 11, 11, 0, 0, 0, 9, 12, 0, 2, 2, 2, 2, 13, 14, 0, + 0, 11, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 16, 17, + 18, 19, 20, 21, 22, 16, 23, 24, 25, 12, 26, 27, 20, 2, 2, 2, + 2, 2, 20, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, + 2, 28, 29, 30, 2, 2, 2, 9, 30, 9, 30, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 9, 0, 2, 2, 0, 17, + 18, 19, 20, 31, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 2, + 30, 2, 0, 0, 0, 0, 0, 9, 36, 12, 15, 30, 2, 2, 9, 0, + 30, 9, 2, 30, 9, 2, 0, 37, 18, 19, 31, 0, 27, 38, 27, 39, + 0, 40, 0, 0, 0, 30, 2, 9, 9, 0, 0, 0, 2, 2, 2, 2, + 2, 41, 42, 43, 0, 0, 0, 0, 0, 12, 15, 30, 2, 2, 2, 2, + 30, 2, 30, 2, 2, 2, 2, 2, 2, 9, 2, 30, 2, 2, 0, 17, + 18, 19, 20, 21, 27, 22, 35, 24, 0, 0, 0, 0, 0, 30, 41, 41, + 44, 12, 29, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 0, 17, + 45, 0, 0, 27, 22, 0, 0, 2, 30, 30, 0, 0, 0, 0, 0, 0, + 0, 0, 46, 30, 2, 2, 9, 0, 2, 9, 2, 2, 0, 30, 9, 9, + 2, 0, 30, 9, 0, 2, 9, 0, 2, 2, 2, 2, 2, 2, 0, 0, + 23, 16, 47, 0, 48, 33, 48, 34, 0, 0, 0, 0, 35, 0, 0, 0, + 0, 15, 29, 49, 2, 2, 2, 9, 2, 9, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 17, 22, 16, 23, 47, 22, 38, 22, 39, + 0, 0, 0, 27, 31, 2, 9, 0, 0, 10, 29, 30, 2, 2, 2, 9, + 2, 2, 2, 30, 2, 2, 0, 17, 45, 0, 0, 35, 47, 0, 0, 0, + 9, 50, 51, 0, 0, 0, 0, 0, 0, 11, 29, 2, 2, 2, 2, 9, + 2, 2, 2, 2, 2, 2, 52, 53, 23, 19, 20, 31, 48, 33, 48, 34, + 54, 0, 0, 0, 35, 0, 0, 0, 30, 12, 29, 30, 2, 2, 2, 2, + 2, 2, 2, 2, 9, 0, 2, 2, 2, 2, 30, 2, 2, 2, 2, 30, + 0, 2, 2, 2, 9, 0, 55, 0, 35, 23, 22, 31, 31, 18, 48, 48, + 25, 0, 23, 0, 0, 0, 0, 0, 0, 2, 0, 2, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 2, 56, 56, 57, 0, 0, + 18, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, + 0, 58, 21, 59, 22, 22, 20, 20, 46, 21, 11, 31, 11, 2, 2, 60, + 61, 61, 61, 61, 61, 62, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 63, 0, 0, 0, 0, 64, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 65, 45, 59, 66, 22, 22, 67, 68, 69, 70, + 71, 2, 2, 2, 2, 2, 1, 0, 5, 2, 2, 2, 23, 20, 2, 2, + 72, 71, 73, 74, 65, 73, 29, 29, 2, 52, 22, 53, 2, 2, 2, 2, + 2, 2, 75, 76, 77, 29, 29, 78, 79, 2, 2, 2, 2, 2, 29, 45, + 0, 2, 59, 80, 0, 0, 0, 0, 30, 2, 59, 47, 0, 0, 0, 0, + 0, 2, 59, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 9, + 2, 9, 59, 0, 0, 0, 0, 0, 0, 2, 2, 81, 45, 22, 59, 20, + 48, 48, 48, 48, 15, 82, 83, 84, 85, 86, 87, 0, 0, 0, 0, 88, + 0, 9, 0, 0, 30, 0, 89, 81, 90, 2, 2, 2, 2, 9, 0, 0, + 0, 42, 42, 91, 92, 2, 2, 2, 2, 2, 2, 2, 2, 13, 9, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 93, 61, 0, + 0, 94, 95, 94, 94, 96, 97, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 29, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, - 0, 2, 2, 2, 52, 99, 45, 0, 0, 2, 2, 100, 101, 102, 103, 61, - 63, 104, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 105, 46, - 40, 11, 106, 74, 2, 2, 2, 2, 2, 2, 2, 107, 22, 20, 20, 22, - 48, 48, 22, 108, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 109, 110, - 110, 110, 110, 0, 0, 0, 0, 0, 0, 106, 74, 2, 2, 2, 2, 2, - 2, 60, 61, 59, 25, 22, 111, 61, 2, 2, 2, 2, 107, 22, 23, 45, - 45, 102, 112, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 113, - 102, 102, 102, 114, 115, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, - 2, 11, 46, 116, 116, 116, 11, 116, 116, 15, 116, 116, 116, 26, 0, 40, - 0, 0, 0, 117, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 118, 0, - 0, 0, 0, 0, 0, 0, 6, 119, 120, 42, 42, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 120, 120, 121, 120, 120, 120, 120, 120, 120, 120, - 120, 0, 0, 122, 0, 0, 0, 0, 0, 0, 7, 122, 0, 0, 0, 0, + 0, 2, 2, 2, 52, 98, 45, 0, 0, 2, 2, 99, 100, 101, 102, 61, + 63, 103, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 104, 46, + 40, 11, 105, 74, 2, 2, 2, 2, 2, 2, 2, 106, 22, 20, 20, 22, + 48, 48, 22, 107, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 108, 109, + 109, 109, 109, 0, 0, 0, 0, 0, 0, 105, 74, 2, 2, 2, 2, 2, + 2, 60, 61, 59, 25, 22, 110, 61, 2, 2, 2, 2, 106, 22, 23, 45, + 45, 101, 111, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 112, + 101, 101, 101, 113, 114, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, + 2, 11, 46, 115, 115, 115, 11, 115, 115, 15, 115, 115, 115, 26, 0, 40, + 0, 0, 0, 116, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 117, 0, + 0, 0, 0, 0, 0, 0, 6, 118, 119, 42, 42, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 119, 119, 120, 119, 119, 119, 119, 119, 119, 119, + 119, 0, 0, 121, 0, 0, 0, 0, 0, 0, 7, 121, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, - 0, 0, 0, 0, 123, 123, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 30, 0, 0, 0, 0, 0, 0, 0, 124, 0, 123, 123, 0, 0, 0, 0, - 0, 2, 53, 2, 108, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, + 0, 0, 0, 0, 122, 122, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 30, 0, 0, 0, 0, 0, 0, 0, 123, 0, 122, 122, 0, 0, 0, 0, + 0, 2, 53, 2, 107, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, 0, 2, 2, 0, 0, 0, 0, 0, 0, 29, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 125, 23, 23, 23, 23, 23, 23, 23, 126, 0, 0, 0, 0, + 2, 2, 2, 124, 23, 23, 23, 23, 23, 23, 23, 125, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 0, 0, 0, 0, 0, - 52, 2, 2, 2, 22, 22, 127, 116, 0, 2, 2, 2, 128, 20, 59, 20, - 113, 102, 129, 0, 0, 0, 0, 0, 0, 11, 130, 2, 2, 2, 2, 2, - 2, 2, 131, 23, 22, 20, 48, 132, 133, 134, 0, 0, 0, 0, 0, 0, + 52, 2, 2, 2, 22, 22, 126, 115, 0, 2, 2, 2, 127, 20, 59, 20, + 112, 101, 128, 0, 0, 0, 0, 0, 0, 11, 129, 2, 2, 2, 2, 2, + 2, 2, 130, 23, 22, 20, 48, 131, 132, 133, 0, 0, 0, 0, 0, 0, 0, 2, 2, 52, 30, 2, 2, 2, 2, 2, 2, 2, 2, 10, 22, 59, - 99, 76, 135, 136, 137, 0, 0, 0, 0, 2, 138, 2, 2, 2, 2, 139, - 0, 30, 2, 42, 5, 0, 79, 15, 2, 53, 22, 140, 52, 53, 2, 2, - 105, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, + 98, 76, 134, 135, 136, 0, 0, 0, 0, 2, 137, 2, 2, 2, 2, 138, + 0, 30, 2, 42, 5, 0, 79, 15, 2, 139, 20, 53, 127, 139, 2, 2, + 140, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, 25, 0, 0, 142, 143, 0, 0, 0, 0, 2, 65, 45, 23, 80, 47, 144, 0, 81, 81, 81, 81, 81, 81, 81, 81, 0, 0, 0, 0, 0, 0, 0, - 6, 120, 120, 120, 120, 121, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, + 6, 119, 119, 119, 119, 120, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 2, 30, 2, 2, 2, 2, 2, 30, 2, 2, 2, 30, 9, - 0, 128, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, + 0, 127, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 0, 27, 22, 22, 30, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, - 0, 2, 2, 2, 116, 116, 116, 116, 116, 148, 2, 9, 0, 0, 0, 0, + 0, 2, 2, 2, 115, 115, 115, 115, 115, 148, 2, 9, 0, 0, 0, 0, 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, 2, 30, 2, 9, 0, 30, 2, 0, 0, 149, 150, 151, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 134, 0, 0, 0, + 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 133, 0, 0, 0, 0, 0, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 2, 2, 2, 2, 2, 53, 52, 53, 0, 0, 0, 0, 153, 11, 74, 2, 2, 2, 2, 2, 2, 18, 19, 21, 16, 24, 37, 0, 0, 0, 31, 0, 0, 0, 0, 0, - 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 128, 20, 22, 154, + 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 127, 20, 22, 154, 22, 21, 155, 156, 2, 2, 2, 2, 2, 0, 0, 65, 157, 0, 0, 0, 0, 2, 13, 0, 0, 0, 0, 0, 0, 2, 65, 25, 20, 20, 20, 22, - 22, 108, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, + 22, 107, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, 19, 22, 22, 161, 44, 0, 0, 0, - 49, 128, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, + 49, 127, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 10, 18, 19, 21, 22, 162, 31, 0, 0, 11, 11, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, @@ -559,70 +557,69 @@ hb_use_u8[3657] = 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 30, 0, 9, 2, 2, 2, 30, 45, 59, 20, 20, 31, 33, 32, 32, 25, 163, 29, 164, 165, 37, 0, 0, 0, 0, 0, 0, 12, 26, 0, 0, 0, 0, 0, - 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 126, 15, 17, 0, 0, 0, + 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 125, 15, 17, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 166, 167, 0, 0, 0, 0, 0, 0, - 0, 18, 19, 20, 20, 66, 99, 25, 160, 11, 168, 9, 0, 0, 0, 0, + 0, 18, 19, 20, 20, 66, 98, 25, 160, 11, 168, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, 169, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, 0, 23, 19, 20, 20, 21, 16, 82, 169, 38, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 10, 170, 25, 20, 22, 22, 168, 9, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 43, 136, 23, 22, 20, 76, 21, 22, 0, + 0, 2, 2, 2, 2, 2, 9, 43, 135, 23, 22, 20, 76, 21, 22, 0, 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, - 19, 20, 21, 22, 105, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, + 19, 20, 21, 22, 104, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 171, 165, 172, 173, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, - 2, 65, 25, 20, 20, 0, 22, 23, 29, 108, 0, 33, 0, 0, 0, 0, - 0, 52, 20, 22, 22, 22, 140, 2, 2, 2, 174, 175, 11, 15, 176, 61, - 177, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, - 2, 2, 2, 158, 158, 158, 178, 178, 178, 178, 178, 178, 15, 179, 0, 30, - 0, 22, 20, 20, 31, 22, 22, 11, 169, 0, 61, 61, 61, 61, 61, 61, - 61, 66, 21, 82, 46, 0, 0, 0, 0, 2, 2, 2, 9, 2, 30, 2, - 2, 52, 22, 22, 31, 0, 38, 22, 27, 11, 159, 180, 181, 0, 0, 0, - 0, 2, 2, 2, 30, 9, 2, 2, 2, 2, 2, 2, 2, 2, 23, 23, - 47, 22, 35, 82, 68, 0, 0, 0, 0, 2, 182, 66, 47, 0, 0, 0, - 0, 11, 183, 2, 2, 2, 2, 2, 2, 2, 2, 23, 22, 20, 31, 0, - 48, 16, 143, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 156, 0, - 0, 184, 184, 184, 184, 184, 184, 184, 184, 185, 185, 185, 186, 187, 185, 184, - 184, 188, 184, 184, 189, 190, 190, 190, 190, 190, 190, 190, 0, 0, 0, 0, - 0, 184, 184, 184, 184, 184, 191, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 22, 22, 22, 22, 22, 22, 192, 193, 194, 11, 11, 11, 46, 0, 0, 0, - 0, 29, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 65, 47, - 0, 2, 2, 2, 2, 2, 9, 0, 58, 195, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, - 40, 116, 26, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, 2, 2, 2, 0, 58, - 37, 0, 6, 120, 120, 120, 121, 0, 0, 11, 11, 11, 49, 2, 2, 2, - 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, - 46, 2, 2, 2, 2, 2, 2, 11, 11, 2, 2, 2, 2, 2, 2, 22, - 22, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 44, 44, 44, 92, 0, - 0, O, O, O, GB, B, B, O, SB, O, SE, GB, O, O, WJ,FMPst, - FMPst, O, CGJ, B, O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv, - VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, - VPst, VPst, H, VPre, VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, - O, O, VPre, VPre, O, VPre, H, O, VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, - H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, - O, VPre, VPre,VMAbv, B, O, CS, CS,VMPst, B, VAbv, VAbv, B, R, O, HVM, - O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB, SUB, O, SUB, SUB, - O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, - B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B, - VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS, - FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, - FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, - SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,SMAbv,SMAbv,SMAbv, VPst, - IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, - CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, H, MPst, VPst, H,VMAbv, VAbv, - VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, - MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O,VMPst, IS, - O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, - B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R, R,CMBlw, - VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, - CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, VPst, MPst, R, MPst,CMBlw, B,FMBlw, VBlw, - VMAbv, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, R, MBlw, GB, VAbv, R, - VMPst, G, G, J, J, J, SB, SE, J, HR, G, G, HM, HM, HM, G, - O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, + 2, 65, 25, 20, 20, 0, 22, 23, 29, 107, 0, 33, 0, 0, 0, 0, + 0, 52, 20, 22, 22, 22, 139, 2, 2, 2, 174, 140, 11, 15, 175, 61, + 176, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, + 2, 2, 2, 158, 158, 158, 177, 177, 177, 177, 177, 177, 15, 178, 0, 30, + 0, 16, 20, 16, 16, 0, 0, 0, 0, 22, 20, 20, 31, 22, 22, 11, + 169, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, + 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, + 27, 11, 159, 179, 180, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, + 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, + 0, 2, 181, 66, 47, 0, 0, 0, 0, 11, 182, 2, 2, 2, 2, 2, + 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 156, 0, 0, 183, 183, 183, 183, 183, 183, 183, + 183, 184, 184, 184, 185, 186, 184, 183, 183, 187, 183, 183, 188, 189, 189, 189, + 189, 189, 189, 189, 0, 0, 0, 0, 0, 183, 183, 183, 183, 183, 190, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 191, 192, + 193, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 65, 47, 0, 2, 2, 2, 2, 2, 9, 0, + 58, 194, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 0, 0, 0, 40, 115, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 119, 119, 119, 120, 0, + 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, + 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, + 20, 2, 2, 44, 44, 44, 92, 0, 0, O, O, O, GB, B, B, O, + SB, O, SE, GB, O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv, + VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, + VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, + VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, + VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, + MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS, + VMPst, B, VAbv, VAbv, B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, + VBlw, B, SUB, SUB, SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv, + VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, + VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv, + FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB, + CMAbv,CMAbv, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, + MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, + B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw, + VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, + H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, + MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, + B, VBlw,VMAbv, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, + IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, + IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, + O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, + VPst, MPst, R, MPst,CMBlw, B,FMBlw, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, + IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, J, HR, + G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, }; -static const uint16_t -hb_use_u16[486] = +static const uint16_t hb_use_u16[488]= { 0, 0, 1, 2, 0, 3, 4, 5, 0, 6, 7, 0, 8, 0, 9, 10, 11, 12, 10, 13, 14, 10, 10, 15, 16, 17, 18, 19, 20, 21, 22, 23, @@ -633,43 +630,43 @@ hb_use_u16[486] = 31, 66, 67, 68, 10, 69, 70, 10, 71, 72, 73, 74, 75, 76, 77, 0, 10, 10, 78, 79, 80, 81, 82, 83, 84, 85, 10, 86, 10, 87, 10, 88, 89, 90, 10, 91, 92, 93, 2, 0, 94, 0, 10, 95, 96, 10, 97, 0, - 98, 99,100,101, 31, 10,102,103,104, 10,105,106, 10,107, 10,108, - 109,110, 2, 2,111, 10, 10,112,113, 2,114,115,116, 10,117, 10, - 118,119,120,121,122, 0, 0,123,124,125, 0,126,127,128,129, 0, - 130,131,132, 0, 0,133,134, 0,135, 0, 0, 10,136,137,138, 0, - 139, 10,140, 0, 10,141,142, 10, 10,143,144, 2,145,146,147, 10, - 148,149,150, 10, 10,151,152, 2,153, 99,154,155,156, 2, 10,157, - 10,158,159, 0,160,161,162, 2,163, 0, 0,164, 0,165, 0,166, - 166,167, 34,168,169,170, 10,171, 95, 0,172, 0, 10,173,174, 0, - 175, 2,176, 10,177, 0,178,173,179,180,181, 0, 0,182,183, 0, - 184, 10, 10,185,186,187,188,189,190, 10, 10,191,192, 0,193, 10, - 194,195,196, 10, 10,197, 10,198,199,106,200,103, 10, 34,201,202, - 203, 0,204,205, 95, 10, 10,206,207, 2,208, 21, 22,209,210,211, - 212,213,214, 10, 10,215,216,217,218, 0, 10,219,220,221,222, 0, - 200, 10, 10,223,224, 2,225,226,227,228, 10,229,230, 2,231,232, - 2, 10,141, 0, 10,233,234,104,235, 0,236,237,238,239, 10,240, - 241, 2,242, 10, 10,243,244, 0,245, 10, 10,246,247,248,249,250, - 22, 10,225,251, 8, 10, 71, 19, 10,252, 74,253,254, 10, 10,255, - 256, 2,257, 10,258,259, 10,260,261, 49, 10,262,263,264,265,265, - 265,266,267,268,265,269, 10,270,271, 2, 10,272,273, 10,274, 2, - 275,276,277,277,278,279,280, 0, 10,177, 0,281,106, 71, 95,282, - 0,283, 71,284,285, 0,286, 0,287, 2,288, 2,289,106,290, 2, - 130,130,163,163,163,130, + 10, 98, 99,100, 31, 10,101,102,103, 10,104,105, 10,106, 10,107, + 108,109, 2, 2,110, 10, 10,111,112, 2,113,114,115, 10,116, 10, + 117,118,119,120,121, 0, 0,122,123,124, 0,125,126,127,128, 0, + 129,130,131, 0, 0,132,133, 0,134, 0, 0, 10,135,136,137, 0, + 138, 10,139, 0, 10,140,141, 10, 10,142,143, 2,144,145,146, 10, + 147,148,149, 10, 10,150,151, 2,152, 98,153,154,155, 2, 10,156, + 10,157,158, 0,159,160,161, 2,162, 0, 0,163, 0,164, 0,165, + 165,166, 34,167,168,169, 10,170, 95, 0,171, 0, 10,172,173, 0, + 174, 2,175, 10,176, 0,177,172,178,179,180, 0, 0,181,182, 0, + 183, 10, 10,184,185,186,187,188,189, 10, 10,190,191, 0,192, 10, + 193,194,195, 10, 10,196, 10,197,198,105,199,102, 10, 34,200,201, + 202, 0,203,204, 95, 10, 10,205,206, 2,207, 21, 22,208,209,210, + 211,212,213, 10, 10,214,215,216,217, 0, 10,218,219,220,221, 0, + 199, 10, 10,222,223, 2,224,225,226,227, 10,228,229, 2,230,231, + 2, 10,140, 0, 10,232,233,103,234, 0,235,236,237,238, 10,239, + 240, 2,241, 10, 10,242,243, 0,244, 10, 10,245,246,247,248,249, + 250, 0, 22, 10,224,251, 8, 10, 71, 19, 10,252, 74,253,254, 10, + 10,255,256, 2,257, 10,258,259, 10,260,261, 49, 10,262,263,264, + 265,265,265,266,267,268,265,269, 10,270,271, 2, 10,272,273, 10, + 274, 2,275,276,277,277,278,279,280, 0, 10,176, 0,281,105, 71, + 95,282, 0,283, 71,284,285, 0,286, 0,287, 2,288, 2,289,105, + 290, 2,129,129,162,162,162,129, }; -static inline unsigned -hb_use_b4 (const uint8_t* a, unsigned i) +static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline uint_fast8_t -hb_use_get_category (unsigned u) +static inline uint8_t hb_use_get_category (unsigned u) { - return u<921600u?hb_use_u8[3265+(((hb_use_u8[937+(((hb_use_u16[((hb_use_u8[369+(((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15u))])<<3)+((u>>1>>3>>1)&7u))])<<1)+((u>>1>>3)&1u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; + return u<921600 ? hb_use_u8[3273u+((hb_use_u8[945u+((hb_use_u16[((hb_use_u8[369u+((hb_use_u8[113u+((hb_use_b4(hb_use_u8,((((((((((u)>>1))>>3))>>1))>>3))>>4)))<<4)+((((((((((u)>>1))>>3))>>1))>>3))&15)])<<3)+((((((((u)>>1))>>3))>>1))&7)])<<1)+((((((u)>>1))>>3))&1)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : O; } + #endif + #undef B #undef CGJ #undef CS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc index 4a112e1c289..bb586a68ce5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper-vowel-constraints.cc @@ -10,8 +10,8 @@ * # Date: 2015-03-12, 21:17:00 GMT [AG] * # Date: 2019-11-08, 23:22:00 GMT [AG] * - * # Scripts-16.0.0.txt - * # Date: 2024-04-30, 21:48:40 GMT + * # Scripts-17.0.0.txt + * # Date: 2025-07-24, 13:28:55 GMT */ #include "hb.hh" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh index 48cd5296ffd..9993507e360 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shaper.hh @@ -396,6 +396,12 @@ hb_ot_shaper_categorize (hb_script_t script, case HB_SCRIPT_TODHRI: case HB_SCRIPT_TULU_TIGALARI: + /* Unicode-17.0 additions */ + case HB_SCRIPT_BERIA_ERFE: + case HB_SCRIPT_SIDETIC: + case HB_SCRIPT_TAI_YO: + case HB_SCRIPT_TOLONG_SIKI: + /* If the designer designed the font for the 'DFLT' script, * (or we ended up arbitrarily pick 'latn'), use the default shaper. * Otherwise, use the specific shaper. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh index 10165f57b75..28bc23d8f01 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh @@ -352,7 +352,7 @@ struct AxisValue { float get_value (unsigned int axis_index) const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_value (); case 2: hb_barrier (); return u.format2.get_value (); @@ -364,7 +364,7 @@ struct AxisValue unsigned int get_axis_index () const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_axis_index (); case 2: hb_barrier (); return u.format2.get_axis_index (); @@ -376,7 +376,7 @@ struct AxisValue hb_ot_name_id_t get_value_name_id () const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_value_name_id (); case 2: hb_barrier (); return u.format2.get_value_name_id (); @@ -389,9 +389,9 @@ struct AxisValue template typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward (ds)...)); @@ -403,7 +403,7 @@ struct AxisValue bool keep_axis_value (const hb_array_t axis_records, hb_hashmap_t *user_axes_location) const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.keep_axis_value (axis_records, user_axes_location); case 2: hb_barrier (); return u.format2.keep_axis_value (axis_records, user_axes_location); @@ -420,7 +420,7 @@ struct AxisValue return_trace (false); hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); @@ -433,14 +433,14 @@ struct AxisValue protected: union { - HBUINT16 format; + struct { HBUINT16 v; } format; AxisValueFormat1 format1; AxisValueFormat2 format2; AxisValueFormat3 format3; AxisValueFormat4 format4; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct AxisValueOffsetArray: UnsizedArrayOf> diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh index 50ddf5f6967..4933e661371 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh @@ -6,8 +6,8 @@ * * on files with these headers: * - * - * File-Date: 2025-01-21 + * + * File-Date: 2025-08-25 */ #ifndef HB_OT_TAG_TABLE_HH @@ -704,7 +704,7 @@ static const LangTag ot_languages3[] = { /*{HB_TAG('g','u','z',' '), HB_TAG('G','U','Z',' ')},*/ /* Gusii */ {HB_TAG('g','w','i',' '), HB_TAG('A','T','H',' ')}, /* Gwichʼin -> Athapaskan */ {HB_TAG('g','y','n',' '), HB_TAG('C','P','P',' ')}, /* Guyanese Creole English -> Creoles */ - {HB_TAG('h','a','a',' '), HB_TAG('A','T','H',' ')}, /* Han -> Athapaskan */ + {HB_TAG('h','a','a',' '), HB_TAG('A','T','H',' ')}, /* Hän -> Athapaskan */ {HB_TAG('h','a','e',' '), HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */ {HB_TAG('h','a','i',' '), HB_TAG('H','A','I','0')}, /* Haida [macrolanguage] */ {HB_TAG('h','a','k',' '), HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese, Simplified */ @@ -921,7 +921,7 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','v','t',' '), HB_TAG('K','R','N',' ')}, /* Lahta Karen -> Karen */ {HB_TAG('k','v','u',' '), HB_TAG('K','R','N',' ')}, /* Yinbaw Karen -> Karen */ {HB_TAG('k','v','y',' '), HB_TAG('K','R','N',' ')}, /* Yintale Karen -> Karen */ -/*{HB_TAG('k','w','k',' '), HB_TAG('K','W','K',' ')},*/ /* Kwakiutl -> Kwakʼwala */ +/*{HB_TAG('k','w','k',' '), HB_TAG('K','W','K',' ')},*/ /* Kwakʼwala */ {HB_TAG('k','w','w',' '), HB_TAG('C','P','P',' ')}, /* Kwinti -> Creoles */ {HB_TAG('k','w','y',' '), HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */ {HB_TAG('k','x','c',' '), HB_TAG('K','M','S',' ')}, /* Konso -> Komso */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh index e26687c34ed..8a9307d6933 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh @@ -143,10 +143,13 @@ struct AxisValueMap struct SegmentMaps : Array16Of { - int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const + float map_float (float value, unsigned int from_offset = 0, unsigned int to_offset = 1) const { -#define fromCoord coords[from_offset].to_int () -#define toCoord coords[to_offset].to_int () +#define fromCoord coords[from_offset].to_float () +#define toCoord coords[to_offset].to_float () + + const auto *map = arrayZ; + /* The following special-cases are not part of OpenType, which requires * that at least -1, 0, and +1 must be mapped. But we include these as * part of a better error recovery scheme. */ @@ -155,47 +158,98 @@ struct SegmentMaps : Array16Of if (!len) return value; else /* len == 1*/ - return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; + return value - map[0].fromCoord + map[0].toCoord; } - if (value <= arrayZ[0].fromCoord) - return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; + // At least two mappings now. - unsigned int i; - unsigned int count = len - 1; - for (i = 1; i < count && value > arrayZ[i].fromCoord; i++) - ; + /* CoreText is wild... + * PingFangUI avar needs all this special-casing... + * So we implement an extended version of the spec here, + * which is more robust and more likely to be compatible with + * the wild. */ - if (value >= arrayZ[i].fromCoord) - return value - arrayZ[i].fromCoord + arrayZ[i].toCoord; + unsigned start = 0; + unsigned end = len; + if (map[start].fromCoord == -1 && map[start].toCoord == -1 && map[start+1].fromCoord == -1) + start++; + if (map[end-1].fromCoord == +1 && map[end-1].toCoord == +1 && map[end-2].fromCoord == +1) + end--; - if (unlikely (arrayZ[i-1].fromCoord == arrayZ[i].fromCoord)) - return arrayZ[i-1].toCoord; + /* Look for exact match first, and do lots of special-casing. */ + unsigned i; + for (i = start; i < end; i++) + if (value == map[i].fromCoord) + break; + if (i < end) + { + // There's at least one exact match. See if there are more. + unsigned j = i; + for (; j + 1 < end; j++) + if (value != map[j + 1].fromCoord) + break; + + // [i,j] inclusive are all exact matches: + + // If there's only one, return it. This is the only spec-compliant case. + if (i == j) + return map[i].toCoord; + // If there's exactly three, return the middle one. + if (i + 2 == j) + return map[i + 1].toCoord; + + // Ignore the middle ones. Return the one mapping closer to 0. + if (value < 0) return map[j].toCoord; + if (value > 0) return map[i].toCoord; + + // Mapping 0? CoreText seems confused. It seems to prefer 0 here... + // So we'll just return the smallest one. lol + return fabsf (map[i].toCoord) < fabsf (map[j].toCoord) ? map[i].toCoord : map[j].toCoord; + + // Mapping 0? Return one not mapping to 0. + if (map[i].toCoord == 0) + return map[j].toCoord; + else + return map[i].toCoord; + } + + /* There's at least two and we're not an exact match. Prepare to lerp. */ + + // Find the segment we're in. + for (i = start; i < end; i++) + if (value < map[i].fromCoord) + break; + + if (i == 0) + { + // Value before all segments; Shift. + return value - map[0].fromCoord + map[0].toCoord; + } + if (i == end) + { + // Value after all segments; Shift. + return value - map[end - 1].fromCoord + map[end - 1].toCoord; + } + + // Actually interpolate. + auto &before = map[i-1]; + auto &after = map[i]; + float denom = after.fromCoord - before.fromCoord; // Can't be zero by now. + return before.toCoord + ((after.toCoord - before.toCoord) * (value - before.fromCoord)) / denom; - int denom = arrayZ[i].fromCoord - arrayZ[i-1].fromCoord; - return roundf (arrayZ[i-1].toCoord + ((float) (arrayZ[i].toCoord - arrayZ[i-1].toCoord) * - (value - arrayZ[i-1].fromCoord)) / denom); #undef toCoord #undef fromCoord } - int unmap (int value) const { return map (value, 1, 0); } + float unmap_float (float value) const { return map_float (value, 1, 0); } + + // TODO Kill this. Triple unmap_axis_range (const Triple& axis_range) const { - F2DOT14 val, unmapped_val; - - val.set_float (axis_range.minimum); - unmapped_val.set_int (unmap (val.to_int ())); - float unmapped_min = unmapped_val.to_float (); - - val.set_float (axis_range.middle); - unmapped_val.set_int (unmap (val.to_int ())); - float unmapped_middle = unmapped_val.to_float (); - - val.set_float (axis_range.maximum); - unmapped_val.set_int (unmap (val.to_int ())); - float unmapped_max = unmapped_val.to_float (); + float unmapped_min = unmap_float (axis_range.minimum); + float unmapped_middle = unmap_float (axis_range.middle); + float unmapped_max = unmap_float (axis_range.maximum); return Triple{(double) unmapped_min, (double) unmapped_middle, (double) unmapped_max}; } @@ -203,6 +257,11 @@ struct SegmentMaps : Array16Of bool subset (hb_subset_context_t *c, hb_tag_t axis_tag) const { TRACE_SUBSET (this); + + /* This function cannot work on avar2 table (and currently doesn't). + * We should instead keep the design coords in the shape plan and use + * those. unmap_axis_range needs to be killed. */ + /* avar mapped normalized axis range*/ Triple *axis_range; if (!c->plan->axes_location.has (axis_tag, &axis_range)) @@ -304,14 +363,14 @@ struct avar return_trace (true); } - void map_coords (int *coords, unsigned int coords_length) const + void map_coords_16_16 (int *coords, unsigned int coords_length) const { unsigned int count = hb_min (coords_length, axisCount); const SegmentMaps *map = &firstAxisSegmentMaps; for (unsigned int i = 0; i < count; i++) { - coords[i] = map->map (coords[i]); + coords[i] = roundf (map->map_float (coords[i] / 65536.f) * 65536.f); map = &StructAfter (*map); } @@ -329,15 +388,20 @@ struct avar const auto &var_store = this+v2.varStore; auto *var_store_cache = var_store.create_cache (); + hb_vector_t coords_2_14; + coords_2_14.resize (coords_length); + for (unsigned i = 0; i < coords_length; i++) + coords_2_14[i] = roundf (coords[i] / 4.f); // 16.16 -> 2.14 + hb_vector_t out; out.alloc (coords_length); for (unsigned i = 0; i < coords_length; i++) { int v = coords[i]; uint32_t varidx = varidx_map.map (i); - float delta = var_store.get_delta (varidx, coords, coords_length, var_store_cache); - v += roundf (delta); - v = hb_clamp (v, -(1<<14), +(1<<14)); + float delta = var_store.get_delta (varidx, coords_2_14.arrayZ, coords_2_14.length, var_store_cache); + v += roundf (delta * 4); // 2.14 -> 16.16 + v = hb_clamp (v, -(1<<16), +(1<<16)); out.push (v); } for (unsigned i = 0; i < coords_length; i++) @@ -347,16 +411,54 @@ struct avar #endif } - void unmap_coords (int *coords, unsigned int coords_length) const + bool has_v2_data () const { return version.major > 1; } + + // axis normalization is done in 2.14 here + // TODO: deprecate this API once fonttools is updated to use 16.16 normalization + bool map_coords_2_14 (float *coords, unsigned int coords_length) const { + hb_vector_t coords_2_14; + if (!coords_2_14.resize (coords_length)) return false; unsigned int count = hb_min (coords_length, axisCount); const SegmentMaps *map = &firstAxisSegmentMaps; for (unsigned int i = 0; i < count; i++) { - coords[i] = map->unmap (coords[i]); + int v = roundf (map->map_float (coords[i]) * 16384.f); + coords_2_14[i] = v; + coords[i] = v / 16384.f; map = &StructAfter (*map); } + +#ifndef HB_NO_AVAR2 + if (version.major < 2) + return true; + hb_barrier (); + + for (; count < axisCount; count++) + map = &StructAfter (*map); + + const auto &v2 = * (const avarV2Tail *) map; + + const auto &varidx_map = this+v2.varIdxMap; + const auto &var_store = this+v2.varStore; + auto *var_store_cache = var_store.create_cache (); + + for (unsigned i = 0; i < coords_length; i++) + { + int v = coords_2_14[i]; + uint32_t varidx = varidx_map.map (i); + float delta = var_store.get_delta (varidx, coords_2_14.arrayZ, coords_2_14.length, var_store_cache); + v += roundf (delta); + v = hb_clamp (v, -(1<<16), +(1<<16)); + coords[i] = v / 16384.f; + } + + OT::ItemVariationStore::destroy_cache (var_store_cache); + return true; +#else + return version.major < 2; +#endif } bool subset (hb_subset_context_t *c) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh index 72deddef213..d0a4224c9b0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-common.hh @@ -27,24 +27,35 @@ #define HB_OT_VAR_COMMON_HH #include "hb-ot-layout-common.hh" +#include "hb-alloc-pool.hh" #include "hb-priority-queue.hh" #include "hb-subset-instancer-iup.hh" namespace OT { +using rebase_tent_result_scratch_t = hb_pair_t; /* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */ struct TupleVariationHeader { friend struct tuple_delta_t; - unsigned get_size (unsigned axis_count) const - { return min_size + get_all_tuples (axis_count).get_size (); } + unsigned get_size (unsigned axis_count_times_2) const + { + // This function is super hot in mega-var-fonts with hundreds of masters. + unsigned ti = tupleIndex; + if (unlikely ((ti & (TupleIndex::EmbeddedPeakTuple | TupleIndex::IntermediateRegion)))) + { + unsigned count = ((ti & TupleIndex::EmbeddedPeakTuple) != 0) + ((ti & TupleIndex::IntermediateRegion) != 0) * 2; + return min_size + count * axis_count_times_2; + } + return min_size; + } unsigned get_data_size () const { return varDataSize; } - const TupleVariationHeader &get_next (unsigned axis_count) const - { return StructAtOffset (this, get_size (axis_count)); } + const TupleVariationHeader &get_next (unsigned axis_count_times_2) const + { return StructAtOffset (this, get_size (axis_count_times_2)); } bool unpack_axis_tuples (unsigned axis_count, const hb_array_t shared_tuples, @@ -53,7 +64,7 @@ struct TupleVariationHeader { const F2DOT14 *peak_tuple = nullptr; if (has_peak ()) - peak_tuple = get_peak_tuple (axis_count).arrayZ; + peak_tuple = get_peak_tuple (axis_count); else { unsigned int index = get_index (); @@ -68,8 +79,8 @@ struct TupleVariationHeader if (has_interm) { - start_tuple = get_start_tuple (axis_count).arrayZ; - end_tuple = get_end_tuple (axis_count).arrayZ; + start_tuple = get_start_tuple (axis_count); + end_tuple = get_end_tuple (axis_count); } for (unsigned i = 0; i < axis_count; i++) @@ -98,88 +109,109 @@ struct TupleVariationHeader return true; } + HB_ALWAYS_INLINE double calculate_scalar (hb_array_t coords, unsigned int coord_count, const hb_array_t shared_tuples, - const hb_vector_t> *shared_tuple_active_idx = nullptr) const + hb_scalar_cache_t *shared_tuple_scalar_cache = nullptr) const { + unsigned tuple_index = tupleIndex; + const F2DOT14 *peak_tuple; - unsigned start_idx = 0; - unsigned end_idx = coord_count; - unsigned step = 1; + bool has_interm = tuple_index & TupleIndex::IntermediateRegion; // Inlined for performance - if (has_peak ()) - peak_tuple = get_peak_tuple (coord_count).arrayZ; + if (unlikely (tuple_index & TupleIndex::EmbeddedPeakTuple)) // Inlined for performance + { + peak_tuple = get_peak_tuple (coord_count); + shared_tuple_scalar_cache = nullptr; + } else { - unsigned int index = get_index (); + unsigned int index = tuple_index & TupleIndex::TupleIndexMask; // Inlined for performance + + float scalar; + if (shared_tuple_scalar_cache && + shared_tuple_scalar_cache->get (index, &scalar)) + { + if (has_interm && (scalar != 0 && scalar != 1.f)) + shared_tuple_scalar_cache = nullptr; + else + return (double) scalar; + } + if (unlikely ((index + 1) * coord_count > shared_tuples.length)) return 0.0; - peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count).arrayZ; + peak_tuple = shared_tuples.arrayZ + (coord_count * index); - if (shared_tuple_active_idx) - { - if (unlikely (index >= shared_tuple_active_idx->length)) - return 0.0; - auto _ = (*shared_tuple_active_idx).arrayZ[index]; - if (_.second != -1) - { - start_idx = _.first; - end_idx = _.second + 1; - step = _.second - _.first; - } - else if (_.first != -1) - { - start_idx = _.first; - end_idx = start_idx + 1; - } - } } const F2DOT14 *start_tuple = nullptr; const F2DOT14 *end_tuple = nullptr; - bool has_interm = has_intermediate (); + if (has_interm) { - start_tuple = get_start_tuple (coord_count).arrayZ; - end_tuple = get_end_tuple (coord_count).arrayZ; + start_tuple = get_start_tuple (coord_count); + end_tuple = get_end_tuple (coord_count); } double scalar = 1.0; - for (unsigned int i = start_idx; i < end_idx; i += step) +#ifndef HB_OPTIMIZE_SIZE +#if HB_FAST_NUM_ACCESS + bool skip = coord_count >= 16; +#endif +#endif + for (unsigned int i = 0; i < coord_count; i++) { +#ifndef HB_OPTIMIZE_SIZE +#if HB_FAST_NUM_ACCESS + if (skip) + { + while (i + 4 <= coord_count && * (HBUINT64LE *) &peak_tuple[i] == 0) + i += 4; + while (i < coord_count && peak_tuple[i].to_int () == 0) + i += 1; + if (i >= coord_count) + break; + } +#endif +#endif + int peak = peak_tuple[i].to_int (); if (!peak) continue; int v = coords[i]; + if (!v) { scalar = 0.0; break; } if (v == peak) continue; if (has_interm) { + shared_tuple_scalar_cache = nullptr; int start = start_tuple[i].to_int (); int end = end_tuple[i].to_int (); if (unlikely (start > peak || peak > end || (start < 0 && end > 0 && peak))) continue; - if (v < start || v > end) return 0.0; + if (v < start || v > end) { scalar = 0.0; break; } if (v < peak) { if (peak != start) scalar *= (double) (v - start) / (peak - start); } else { if (peak != end) scalar *= (double) (end - v) / (end - peak); } } - else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.0; + else if (v < hb_min (0, peak) || v > hb_max (0, peak)) { scalar = 0.0; break; } else scalar *= (double) v / peak; } + if (shared_tuple_scalar_cache) + shared_tuple_scalar_cache->set (get_index (), scalar); return scalar; } - bool has_peak () const { return tupleIndex & TuppleIndex::EmbeddedPeakTuple; } - bool has_intermediate () const { return tupleIndex & TuppleIndex::IntermediateRegion; } - bool has_private_points () const { return tupleIndex & TuppleIndex::PrivatePointNumbers; } - unsigned get_index () const { return tupleIndex & TuppleIndex::TupleIndexMask; } + bool has_peak () const { return tupleIndex & TupleIndex::EmbeddedPeakTuple; } + bool has_intermediate () const { return tupleIndex & TupleIndex::IntermediateRegion; } + bool has_private_points () const { return tupleIndex & TupleIndex::PrivatePointNumbers; } + unsigned get_index () const { return tupleIndex & TupleIndex::TupleIndexMask; } protected: - struct TuppleIndex : HBUINT16 + struct TupleIndex : HBUINT16 { enum Flags { EmbeddedPeakTuple = 0x8000u, @@ -188,22 +220,24 @@ struct TupleVariationHeader TupleIndexMask = 0x0FFFu }; - TuppleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } + TupleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } DEFINE_SIZE_STATIC (2); }; hb_array_t get_all_tuples (unsigned axis_count) const { return StructAfter> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); } - hb_array_t get_peak_tuple (unsigned axis_count) const - { return get_all_tuples (axis_count).sub_array (0, axis_count); } - hb_array_t get_start_tuple (unsigned axis_count) const - { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count, axis_count); } - hb_array_t get_end_tuple (unsigned axis_count) const - { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count + axis_count, axis_count); } + const F2DOT14* get_all_tuples_base (unsigned axis_count) const + { return StructAfter> (tupleIndex).arrayZ; } + const F2DOT14* get_peak_tuple (unsigned axis_count) const + { return get_all_tuples_base (axis_count); } + const F2DOT14* get_start_tuple (unsigned axis_count) const + { return get_all_tuples_base (axis_count) + has_peak () * axis_count; } + const F2DOT14* get_end_tuple (unsigned axis_count) const + { return get_all_tuples_base (axis_count) + has_peak () * axis_count + axis_count; } HBUINT16 varDataSize; /* The size in bytes of the serialized * data for this tuple variation table. */ - TuppleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). + TupleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). The low 12 bits are an index into a shared tuple records array. */ /* UnsizedArrayOf peakTuple - optional */ @@ -221,6 +255,21 @@ struct TupleVariationHeader DEFINE_SIZE_MIN (4); }; +struct optimize_scratch_t +{ + iup_scratch_t iup; + hb_vector_t opt_indices; + hb_vector_t rounded_x_deltas; + hb_vector_t rounded_y_deltas; + hb_vector_t opt_deltas_x; + hb_vector_t opt_deltas_y; + hb_vector_t opt_point_data; + hb_vector_t opt_deltas_data; + hb_vector_t point_data; + hb_vector_t deltas_data; + hb_vector_t rounded_deltas; +}; + struct tuple_delta_t { static constexpr bool realloc_move = true; // Watch out when adding new members! @@ -241,11 +290,12 @@ struct tuple_delta_t hb_vector_t compiled_tuple_header; hb_vector_t compiled_deltas; - /* compiled peak coords, empty for non-gvar tuples */ - hb_vector_t compiled_peak_coords; + hb_vector_t compiled_peak_coords; + hb_vector_t compiled_interm_coords; - tuple_delta_t () = default; + tuple_delta_t (hb_alloc_pool_t *pool = nullptr) {} tuple_delta_t (const tuple_delta_t& o) = default; + tuple_delta_t& operator = (const tuple_delta_t& o) = default; friend void swap (tuple_delta_t& a, tuple_delta_t& b) noexcept { @@ -267,6 +317,18 @@ struct tuple_delta_t return *this; } + void copy_from (const tuple_delta_t& o, hb_alloc_pool_t *pool = nullptr) + { + axis_tuples = o.axis_tuples; + indices.duplicate_vector_from_pool (pool, o.indices); + deltas_x.duplicate_vector_from_pool (pool, o.deltas_x); + deltas_y.duplicate_vector_from_pool (pool, o.deltas_y); + compiled_tuple_header.duplicate_vector_from_pool (pool, o.compiled_tuple_header); + compiled_deltas.duplicate_vector_from_pool (pool, o.compiled_deltas); + compiled_peak_coords.duplicate_vector_from_pool (pool, o.compiled_peak_coords); + compiled_interm_coords.duplicate_vector_from_pool (pool, o.compiled_interm_coords); + } + void remove_axis (hb_tag_t axis_tag) { axis_tuples.del (axis_tag); } @@ -321,31 +383,44 @@ struct tuple_delta_t return *this; } - hb_vector_t change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit, - TripleDistances axis_triple_distances) const + void change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit, + TripleDistances axis_triple_distances, + hb_vector_t& out, + rebase_tent_result_scratch_t &scratch, + hb_alloc_pool_t *pool = nullptr) { - hb_vector_t out; + // May move *this out. + + out.reset (); Triple *tent; if (!axis_tuples.has (axis_tag, &tent)) { - out.push (*this); - return out; + out.push (std::move (*this)); + return; } if ((tent->minimum < 0.0 && tent->maximum > 0.0) || !(tent->minimum <= tent->middle && tent->middle <= tent->maximum)) - return out; + return; if (tent->middle == 0.0) { - out.push (*this); - return out; + out.push (std::move (*this)); + return; } - rebase_tent_result_t solutions = rebase_tent (*tent, axis_limit, axis_triple_distances); - for (auto &t : solutions) + rebase_tent_result_t &solutions = scratch.first; + rebase_tent (*tent, axis_limit, axis_triple_distances, solutions, scratch.second); + for (unsigned i = 0; i < solutions.length; i++) { - tuple_delta_t new_var = *this; + auto &t = solutions.arrayZ[i]; + + tuple_delta_t new_var; + if (i < solutions.length - 1) + new_var.copy_from (*this, pool); + else + new_var = std::move (*this); + if (t.second == Triple ()) new_var.remove_axis (axis_tag); else @@ -354,38 +429,76 @@ struct tuple_delta_t new_var *= t.first; out.push (std::move (new_var)); } - - return out; } - bool compile_peak_coords (const hb_map_t& axes_index_map, - const hb_map_t& axes_old_index_tag_map) + bool compile_coords (const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map, + hb_alloc_pool_t *pool= nullptr) { - unsigned axis_count = axes_index_map.get_population (); - if (unlikely (!compiled_peak_coords.alloc (axis_count * F2DOT14::static_size))) + unsigned cur_axis_count = axes_index_map.get_population (); + if (pool) + { + if (unlikely (!compiled_peak_coords.allocate_from_pool (pool, cur_axis_count))) + return false; + } + else if (unlikely (!compiled_peak_coords.resize (cur_axis_count))) return false; + hb_array_t start_coords, end_coords; + unsigned orig_axis_count = axes_old_index_tag_map.get_population (); + unsigned j = 0; for (unsigned i = 0; i < orig_axis_count; i++) { if (!axes_index_map.has (i)) continue; hb_tag_t axis_tag = axes_old_index_tag_map.get (i); - Triple *coords; - F2DOT14 peak_coord; + Triple *coords = nullptr; if (axis_tuples.has (axis_tag, &coords)) - peak_coord.set_float (coords->middle); - else - peak_coord.set_int (0); + { + float min_val = coords->minimum; + float val = coords->middle; + float max_val = coords->maximum; - /* push F2DOT14 value into char vector */ - int16_t val = peak_coord.to_int (); - compiled_peak_coords.push (static_cast (val >> 8)); - compiled_peak_coords.push (static_cast (val & 0xFF)); + compiled_peak_coords.arrayZ[j].set_float (val); + + if (min_val != hb_min (val, 0.f) || max_val != hb_max (val, 0.f)) + { + if (!compiled_interm_coords) + { + if (pool) + { + if (unlikely (!compiled_interm_coords.allocate_from_pool (pool, 2 * cur_axis_count))) + return false; + } + else if (unlikely (!compiled_interm_coords.resize (2 * cur_axis_count))) + return false; + start_coords = compiled_interm_coords.as_array ().sub_array (0, cur_axis_count); + end_coords = compiled_interm_coords.as_array ().sub_array (cur_axis_count); + + for (unsigned k = 0; k < j; k++) + { + signed peak = compiled_peak_coords.arrayZ[k].to_int (); + if (!peak) continue; + start_coords.arrayZ[k].set_int (hb_min (peak, 0)); + end_coords.arrayZ[k].set_int (hb_max (peak, 0)); + } + } + + } + + if (compiled_interm_coords) + { + start_coords.arrayZ[j].set_float (min_val); + end_coords.arrayZ[j].set_float (max_val); + } + } + + j++; } - return !compiled_peak_coords.in_error (); + return !compiled_peak_coords.in_error () && !compiled_interm_coords.in_error (); } /* deltas should be compiled already before we compile tuple @@ -394,7 +507,8 @@ struct tuple_delta_t bool compile_tuple_var_header (const hb_map_t& axes_index_map, unsigned points_data_length, const hb_map_t& axes_old_index_tag_map, - const hb_hashmap_t*, unsigned>* shared_tuples_idx_map) + const hb_hashmap_t*, unsigned>* shared_tuples_idx_map, + hb_alloc_pool_t *pool = nullptr) { /* compiled_deltas could be empty after iup delta optimization, we can skip * compiling this tuple and return true */ @@ -403,7 +517,7 @@ struct tuple_delta_t unsigned cur_axis_count = axes_index_map.get_population (); /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */ unsigned alloc_len = 3 * cur_axis_count * (F2DOT14::static_size) + 4; - if (unlikely (!compiled_tuple_header.resize (alloc_len))) return false; + if (unlikely (!compiled_tuple_header.allocate_from_pool (pool, alloc_len, false))) return false; unsigned flag = 0; /* skip the first 4 header bytes: variationDataSize+tupleIndex */ @@ -411,6 +525,9 @@ struct tuple_delta_t F2DOT14* end = reinterpret_cast (compiled_tuple_header.end ()); hb_array_t coords (p, end - p); + if (!shared_tuples_idx_map) + compile_coords (axes_index_map, axes_old_index_tag_map); // non-gvar tuples do not have compiled coords yet + /* encode peak coords */ unsigned peak_count = 0; unsigned *shared_tuple_idx; @@ -421,16 +538,16 @@ struct tuple_delta_t } else { - peak_count = encode_peak_coords(coords, flag, axes_index_map, axes_old_index_tag_map); + peak_count = encode_peak_coords(coords, flag); if (!peak_count) return false; } /* encode interim coords, it's optional so returned num could be 0 */ - unsigned interim_count = encode_interm_coords (coords.sub_array (peak_count), flag, axes_index_map, axes_old_index_tag_map); + unsigned interim_count = encode_interm_coords (coords.sub_array (peak_count), flag); /* pointdata length = 0 implies "use shared points" */ if (points_data_length) - flag |= TupleVariationHeader::TuppleIndex::PrivatePointNumbers; + flag |= TupleVariationHeader::TupleIndex::PrivatePointNumbers; unsigned serialized_data_size = points_data_length + compiled_deltas.length; TupleVariationHeader *o = reinterpret_cast (compiled_tuple_header.begin ()); @@ -438,105 +555,63 @@ struct tuple_delta_t o->tupleIndex = flag; unsigned total_header_len = 4 + (peak_count + interim_count) * (F2DOT14::static_size); - return compiled_tuple_header.resize (total_header_len); + compiled_tuple_header.shrink_back_to_pool (pool, total_header_len); + return true; } unsigned encode_peak_coords (hb_array_t peak_coords, - unsigned& flag, - const hb_map_t& axes_index_map, - const hb_map_t& axes_old_index_tag_map) const + unsigned& flag) const { - unsigned orig_axis_count = axes_old_index_tag_map.get_population (); - auto it = peak_coords.iter (); - unsigned count = 0; - for (unsigned i = 0; i < orig_axis_count; i++) - { - if (!axes_index_map.has (i)) /* axis pinned */ - continue; - hb_tag_t axis_tag = axes_old_index_tag_map.get (i); - Triple *coords; - if (!axis_tuples.has (axis_tag, &coords)) - (*it).set_int (0); - else - (*it).set_float (coords->middle); - it++; - count++; - } - flag |= TupleVariationHeader::TuppleIndex::EmbeddedPeakTuple; - return count; + hb_memcpy (&peak_coords[0], &compiled_peak_coords[0], compiled_peak_coords.length * sizeof (compiled_peak_coords[0])); + flag |= TupleVariationHeader::TupleIndex::EmbeddedPeakTuple; + return compiled_peak_coords.length; } /* if no need to encode intermediate coords, then just return p */ unsigned encode_interm_coords (hb_array_t coords, - unsigned& flag, - const hb_map_t& axes_index_map, - const hb_map_t& axes_old_index_tag_map) const + unsigned& flag) const { - unsigned orig_axis_count = axes_old_index_tag_map.get_population (); - unsigned cur_axis_count = axes_index_map.get_population (); - - auto start_coords_iter = coords.sub_array (0, cur_axis_count).iter (); - auto end_coords_iter = coords.sub_array (cur_axis_count).iter (); - bool encode_needed = false; - unsigned count = 0; - for (unsigned i = 0; i < orig_axis_count; i++) + if (compiled_interm_coords) { - if (!axes_index_map.has (i)) /* axis pinned */ - continue; - hb_tag_t axis_tag = axes_old_index_tag_map.get (i); - Triple *coords; - float min_val = 0.f, val = 0.f, max_val = 0.f; - if (axis_tuples.has (axis_tag, &coords)) - { - min_val = coords->minimum; - val = coords->middle; - max_val = coords->maximum; - } - - (*start_coords_iter).set_float (min_val); - (*end_coords_iter).set_float (max_val); - - start_coords_iter++; - end_coords_iter++; - count += 2; - if (min_val != hb_min (val, 0.f) || max_val != hb_max (val, 0.f)) - encode_needed = true; + hb_memcpy (&coords[0], &compiled_interm_coords[0], compiled_interm_coords.length * sizeof (compiled_interm_coords[0])); + flag |= TupleVariationHeader::TupleIndex::IntermediateRegion; } - - if (encode_needed) - { - flag |= TupleVariationHeader::TuppleIndex::IntermediateRegion; - return count; - } - return 0; + return compiled_interm_coords.length; } - bool compile_deltas () - { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); } + bool compile_deltas (hb_vector_t &rounded_deltas_scratch, + hb_alloc_pool_t *pool = nullptr) + { return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas, rounded_deltas_scratch, pool); } static bool compile_deltas (hb_array_t point_indices, hb_array_t x_deltas, hb_array_t y_deltas, - hb_vector_t &compiled_deltas /* OUT */) + hb_vector_t &compiled_deltas, /* OUT */ + hb_vector_t &rounded_deltas, /* scratch */ + hb_alloc_pool_t *pool = nullptr) { - hb_vector_t rounded_deltas; - if (unlikely (!rounded_deltas.alloc (point_indices.length))) + if (unlikely (!rounded_deltas.resize_dirty (point_indices.length))) return false; + unsigned j = 0; for (unsigned i = 0; i < point_indices.length; i++) { if (!point_indices[i]) continue; - int rounded_delta = (int) roundf (x_deltas.arrayZ[i]); - rounded_deltas.push (rounded_delta); + rounded_deltas.arrayZ[j++] = (int) roundf (x_deltas.arrayZ[i]); } + rounded_deltas.resize (j); if (!rounded_deltas) return true; - /* allocate enough memories 5 * num_deltas */ - unsigned alloc_len = 5 * rounded_deltas.length; + /* Allocate enough memory: this is the correct bound: + * Worst case scenario is that each delta has to be encoded in 4 bytes, and there + * are runs of 64 items each. Any delta encoded in less than 4 bytes (2, 1, or 0) + * is still smaller than the 4-byte encoding even with their control byte. + * The initial 2 is to handle length==0, for both x and y deltas. */ + unsigned alloc_len = 2 + 4 * rounded_deltas.length + (rounded_deltas.length + 63) / 64; if (y_deltas) alloc_len *= 2; - if (unlikely (!compiled_deltas.resize (alloc_len))) return false; + if (unlikely (!compiled_deltas.allocate_from_pool (pool, alloc_len, false))) return false; unsigned encoded_len = compile_deltas (compiled_deltas, rounded_deltas); @@ -557,28 +632,30 @@ struct tuple_delta_t if (j != rounded_deltas.length) return false; encoded_len += compile_deltas (compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); } - return compiled_deltas.resize (encoded_len); + compiled_deltas.shrink_back_to_pool (pool, encoded_len); + return true; } static unsigned compile_deltas (hb_array_t encoded_bytes, hb_array_t deltas) { - return TupleValues::compile (deltas, encoded_bytes); + return TupleValues::compile_unsafe (deltas, encoded_bytes); } - bool calc_inferred_deltas (const contour_point_vector_t& orig_points) + bool calc_inferred_deltas (const contour_point_vector_t& orig_points, + hb_vector_t &scratch) { unsigned point_count = orig_points.length; if (point_count != indices.length) return false; unsigned ref_count = 0; - hb_vector_t end_points; + + hb_vector_t &end_points = scratch.reset (); for (unsigned i = 0; i < point_count; i++) { - if (indices.arrayZ[i]) - ref_count++; + ref_count += indices.arrayZ[i]; if (orig_points.arrayZ[i].is_end_point) end_points.push (i); } @@ -587,7 +664,7 @@ struct tuple_delta_t return true; if (unlikely (end_points.in_error ())) return false; - hb_set_t inferred_idxes; + hb_bit_set_t inferred_idxes; unsigned start_point = 0; for (unsigned end_point : end_points) { @@ -661,6 +738,7 @@ struct tuple_delta_t bool optimize (const contour_point_vector_t& contour_points, bool is_composite, + optimize_scratch_t &scratch, double tolerance = 0.5 + 1e-10) { unsigned count = contour_points.length; @@ -668,22 +746,21 @@ struct tuple_delta_t deltas_y.length != count) return false; - hb_vector_t opt_indices; - hb_vector_t rounded_x_deltas, rounded_y_deltas; + hb_vector_t &opt_indices = scratch.opt_indices.reset (); + hb_vector_t &rounded_x_deltas = scratch.rounded_x_deltas; + hb_vector_t &rounded_y_deltas = scratch.rounded_y_deltas; - if (unlikely (!rounded_x_deltas.alloc (count) || - !rounded_y_deltas.alloc (count))) + if (unlikely (!rounded_x_deltas.resize_dirty (count) || + !rounded_y_deltas.resize_dirty (count))) return false; for (unsigned i = 0; i < count; i++) { - int rounded_x_delta = (int) roundf (deltas_x.arrayZ[i]); - int rounded_y_delta = (int) roundf (deltas_y.arrayZ[i]); - rounded_x_deltas.push (rounded_x_delta); - rounded_y_deltas.push (rounded_y_delta); + rounded_x_deltas.arrayZ[i] = (int) roundf (deltas_x.arrayZ[i]); + rounded_y_deltas.arrayZ[i] = (int) roundf (deltas_y.arrayZ[i]); } - if (!iup_delta_optimize (contour_points, rounded_x_deltas, rounded_y_deltas, opt_indices, tolerance)) + if (!iup_delta_optimize (contour_points, rounded_x_deltas, rounded_y_deltas, opt_indices, scratch.iup, tolerance)) return false; unsigned ref_count = 0; @@ -692,7 +769,8 @@ struct tuple_delta_t if (ref_count == count) return true; - hb_vector_t opt_deltas_x, opt_deltas_y; + hb_vector_t &opt_deltas_x = scratch.opt_deltas_x.reset (); + hb_vector_t &opt_deltas_y = scratch.opt_deltas_y.reset (); bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0); if (is_comp_glyph_wo_deltas) { @@ -705,34 +783,31 @@ struct tuple_delta_t opt_indices.arrayZ[i] = false; } - hb_vector_t opt_point_data; + hb_vector_t &opt_point_data = scratch.opt_point_data.reset (); if (!compile_point_set (opt_indices, opt_point_data)) return false; - hb_vector_t opt_deltas_data; + hb_vector_t &opt_deltas_data = scratch.opt_deltas_data.reset (); if (!compile_deltas (opt_indices, is_comp_glyph_wo_deltas ? opt_deltas_x : deltas_x, is_comp_glyph_wo_deltas ? opt_deltas_y : deltas_y, - opt_deltas_data)) + opt_deltas_data, + scratch.rounded_deltas)) return false; - hb_vector_t point_data; + hb_vector_t &point_data = scratch.point_data.reset (); if (!compile_point_set (indices, point_data)) return false; - hb_vector_t deltas_data; - if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data)) + hb_vector_t &deltas_data = scratch.deltas_data.reset (); + if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data, scratch.rounded_deltas)) return false; if (opt_point_data.length + opt_deltas_data.length < point_data.length + deltas_data.length) { - indices.fini (); indices = std::move (opt_indices); if (is_comp_glyph_wo_deltas) { - deltas_x.fini (); deltas_x = std::move (opt_deltas_x); - - deltas_y.fini (); deltas_y = std::move (opt_deltas_y); } } @@ -757,7 +832,7 @@ struct tuple_delta_t /* allocate enough memories: 2 bytes for count + 3 bytes for each point */ unsigned num_bytes = 2 + 3 *num_points; - if (unlikely (!compiled_points.resize (num_bytes, false))) + if (unlikely (!compiled_points.resize_dirty (num_bytes))) return false; unsigned pos = 0; @@ -821,7 +896,7 @@ struct tuple_delta_t else compiled_points.arrayZ[header_pos] = (run_length - 1) | 0x80; } - return compiled_points.resize (pos, false); + return compiled_points.resize_dirty (pos); } static double infer_delta (double target_val, double prev_val, double next_val, double prev_delta, double next_delta) @@ -852,15 +927,15 @@ struct TupleVariationData return_trace (c->check_struct (this)); } - unsigned get_size (unsigned axis_count) const + unsigned get_size (unsigned axis_count_times_2) const { unsigned total_size = min_size; unsigned count = tupleVarCount.get_count (); const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header()); for (unsigned i = 0; i < count; i++) { - total_size += tuple_var_header->get_size (axis_count) + tuple_var_header->get_data_size (); - tuple_var_header = &tuple_var_header->get_next (axis_count); + total_size += tuple_var_header->get_size (axis_count_times_2) + tuple_var_header->get_data_size (); + tuple_var_header = &tuple_var_header->get_next (axis_count_times_2); } return total_size; @@ -925,8 +1000,12 @@ struct TupleVariationData const hb_map_t *axes_old_index_tag_map, const hb_vector_t &shared_indices, const hb_array_t shared_tuples, - bool is_composite_glyph) + hb_alloc_pool_t *pool = nullptr, + bool is_composite_glyph = false) { + hb_vector_t private_indices; + hb_vector_t deltas_x; + hb_vector_t deltas_y; do { const HBUINT8 *p = iterator.get_serialized_data (); @@ -939,7 +1018,7 @@ struct TupleVariationData || axis_tuples.is_empty ()) return false; - hb_vector_t private_indices; + private_indices.reset (); bool has_private_points = iterator.current_tuple->has_private_points (); const HBUINT8 *end = p + length; if (has_private_points && @@ -950,27 +1029,24 @@ struct TupleVariationData bool apply_to_all = (indices.length == 0); unsigned num_deltas = apply_to_all ? point_count : indices.length; - hb_vector_t deltas_x; - - if (unlikely (!deltas_x.resize (num_deltas, false) || + if (unlikely (!deltas_x.resize_dirty (num_deltas) || !TupleVariationData::decompile_deltas (p, deltas_x, end))) return false; - hb_vector_t deltas_y; if (is_gvar) { - if (unlikely (!deltas_y.resize (num_deltas, false) || + if (unlikely (!deltas_y.resize_dirty (num_deltas) || !TupleVariationData::decompile_deltas (p, deltas_y, end))) return false; } tuple_delta_t var; var.axis_tuples = std::move (axis_tuples); - if (unlikely (!var.indices.resize (point_count) || - !var.deltas_x.resize (point_count, false))) + if (unlikely (!var.indices.allocate_from_pool (pool, point_count) || + !var.deltas_x.allocate_from_pool (pool, point_count, false))) return false; - if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false))) + if (is_gvar && unlikely (!var.deltas_y.allocate_from_pool (pool, point_count, false))) return false; for (unsigned i = 0; i < num_deltas; i++) @@ -1012,8 +1088,8 @@ struct TupleVariationData /* In VarData, deltas are organized in rows, convert them into * column(region) based tuples, resize deltas_x first */ tuple_delta_t tuple; - if (!tuple.deltas_x.resize (item_count, false) || - !tuple.indices.resize (item_count, false)) + if (!tuple.deltas_x.resize_dirty (item_count) || + !tuple.indices.resize_dirty (item_count)) return false; for (unsigned i = 0; i < item_count; i++) @@ -1041,7 +1117,8 @@ struct TupleVariationData } bool change_tuple_variations_axis_limits (const hb_hashmap_t& normalized_axes_location, - const hb_hashmap_t& axes_triple_distances) + const hb_hashmap_t& axes_triple_distances, + hb_alloc_pool_t *pool = nullptr) { /* sort axis_tag/axis_limits, make result deterministic */ hb_vector_t axis_tags; @@ -1050,6 +1127,10 @@ struct TupleVariationData for (auto t : normalized_axes_location.keys ()) axis_tags.push (t); + // Reused vectors for reduced malloc pressure. + rebase_tent_result_scratch_t scratch; + hb_vector_t out; + axis_tags.qsort (_cmp_axis_tag); for (auto axis_tag : axis_tags) { @@ -1061,9 +1142,10 @@ struct TupleVariationData axis_triple_distances = axes_triple_distances.get (axis_tag); hb_vector_t new_vars; - for (const tuple_delta_t& var : tuple_vars) + for (tuple_delta_t& var : tuple_vars) { - hb_vector_t out = var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances); + // This may move var out. + var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances, out, scratch, pool); if (!out) continue; unsigned new_len = new_vars.length + out.length; @@ -1074,7 +1156,6 @@ struct TupleVariationData for (unsigned i = 0; i < out.length; i++) new_vars.push (std::move (out[i])); } - tuple_vars.fini (); tuple_vars = std::move (new_vars); } return true; @@ -1085,9 +1166,12 @@ struct TupleVariationData bool merge_tuple_variations (contour_point_vector_t* contour_points = nullptr) { hb_vector_t new_vars; + // The pre-allocation is essential for address stability of pointers + // we store in the hashmap. + if (unlikely (!new_vars.alloc (tuple_vars.length))) + return false; hb_hashmap_t*, unsigned> m; - unsigned i = 0; - for (const tuple_delta_t& var : tuple_vars) + for (tuple_delta_t& var : tuple_vars) { /* if all axes are pinned, drop the tuple variation */ if (var.axis_tuples.is_empty ()) @@ -1107,13 +1191,17 @@ struct TupleVariationData } else { - new_vars.push (var); - if (!m.set (&(var.axis_tuples), i)) + auto *new_var = new_vars.push (); + if (unlikely (new_vars.in_error ())) + return false; + hb_swap (*new_var, var); + if (unlikely (!m.set (&(new_var->axis_tuples), new_vars.length - 1))) return false; - i++; } } - tuple_vars.fini (); + m.fini (); // Just in case, since it points into new_vars data. + // Shouldn't be necessary though, since we only move new_vars, not its + // contents. tuple_vars = std::move (new_vars); return true; } @@ -1173,20 +1261,22 @@ struct TupleVariationData } } - bool calc_inferred_deltas (const contour_point_vector_t& contour_points) + bool calc_inferred_deltas (const contour_point_vector_t& contour_points, + hb_vector_t &scratch) { for (tuple_delta_t& var : tuple_vars) - if (!var.calc_inferred_deltas (contour_points)) + if (!var.calc_inferred_deltas (contour_points, scratch)) return false; return true; } - bool iup_optimize (const contour_point_vector_t& contour_points) + bool iup_optimize (const contour_point_vector_t& contour_points, + optimize_scratch_t &scratch) { for (tuple_delta_t& var : tuple_vars) { - if (!var.optimize (contour_points, is_composite)) + if (!var.optimize (contour_points, is_composite, scratch)) return false; } return true; @@ -1195,16 +1285,21 @@ struct TupleVariationData public: bool instantiate (const hb_hashmap_t& normalized_axes_location, const hb_hashmap_t& axes_triple_distances, + optimize_scratch_t &scratch, + hb_alloc_pool_t *pool = nullptr, contour_point_vector_t* contour_points = nullptr, bool optimize = false) { if (!tuple_vars) return true; - if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances)) + if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances, pool)) return false; /* compute inferred deltas only for gvar */ if (contour_points) - if (!calc_inferred_deltas (*contour_points)) + { + hb_vector_t scratch; + if (!calc_inferred_deltas (*contour_points, scratch)) return false; + } /* if iup delta opt is on, contour_points can't be null */ if (optimize && !contour_points) @@ -1213,7 +1308,7 @@ struct TupleVariationData if (!merge_tuple_variations (optimize ? contour_points : nullptr)) return false; - if (optimize && !iup_optimize (*contour_points)) return false; + if (optimize && !iup_optimize (*contour_points, scratch)) return false; return !tuple_vars.in_error (); } @@ -1221,7 +1316,8 @@ struct TupleVariationData const hb_map_t& axes_old_index_tag_map, bool use_shared_points, bool is_gvar = false, - const hb_hashmap_t*, unsigned>* shared_tuples_idx_map = nullptr) + const hb_hashmap_t*, unsigned>* shared_tuples_idx_map = nullptr, + hb_alloc_pool_t *pool = nullptr) { // return true for empty glyph if (!tuple_vars) @@ -1241,6 +1337,7 @@ struct TupleVariationData if (shared_points_bytes) compiled_byte_size += shared_points_bytes->length; } + hb_vector_t rounded_deltas_scratch; // compile delta and tuple var header for each tuple variation for (auto& tuple: tuple_vars) { @@ -1254,12 +1351,13 @@ struct TupleVariationData * this tuple */ if (!points_data->length) continue; - if (!tuple.compile_deltas ()) + if (!tuple.compile_deltas (rounded_deltas_scratch, pool)) return false; unsigned points_data_length = (points_data != shared_points_bytes) ? points_data->length : 0; if (!tuple.compile_tuple_var_header (axes_index_map, points_data_length, axes_old_index_tag_map, - shared_tuples_idx_map)) + shared_tuples_idx_map, + pool)) return false; compiled_byte_size += tuple.compiled_tuple_header.length + points_data_length + tuple.compiled_deltas.length; } @@ -1330,8 +1428,9 @@ struct TupleVariationData { var_data_bytes = var_data_bytes_; var_data = var_data_bytes_.as (); - index = 0; + tuples_left = var_data->tupleVarCount.get_count (); axis_count = axis_count_; + axis_count_times_2 = axis_count_ * 2; current_tuple = &var_data->get_tuple_var_header (); data_offset = 0; table_base = table_base_; @@ -1349,30 +1448,42 @@ struct TupleVariationData return true; } - bool is_valid () const + bool is_valid () { - return (index < var_data->tupleVarCount.get_count ()) && - var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) && - var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (), - current_tuple->get_size (axis_count))); + if (unlikely (tuples_left <= 0)) + return false; + + current_tuple_size = TupleVariationHeader::min_size; + if (unlikely (!var_data_bytes.check_end ((const char *) current_tuple + current_tuple_size))) + return false; + + current_tuple_size = current_tuple->get_size (axis_count_times_2); + if (unlikely (!var_data_bytes.check_end ((const char *) current_tuple + current_tuple_size))) + return false; + + return true; } + HB_ALWAYS_INLINE bool move_to_next () { data_offset += current_tuple->get_data_size (); - current_tuple = ¤t_tuple->get_next (axis_count); - index++; + current_tuple = &StructAtOffset (current_tuple, current_tuple_size); + tuples_left--; return is_valid (); } + // TODO: Make it return (sanitized) hb_bytes_t const HBUINT8 *get_serialized_data () const { return &(table_base+var_data->data) + data_offset; } private: + signed tuples_left; const TupleVariationData *var_data; - unsigned int index; unsigned int axis_count; + unsigned int axis_count_times_2; unsigned int data_offset; + unsigned int current_tuple_size; const void *table_base; public: @@ -1411,7 +1522,7 @@ struct TupleVariationData if (unlikely (p + 1 > end)) return false; count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; } - if (unlikely (!points.resize (count, false))) return false; + if (unlikely (!points.resize_dirty (count))) return false; unsigned n = 0; unsigned i = 0; @@ -1446,12 +1557,14 @@ struct TupleVariationData } template + HB_ALWAYS_INLINE static bool decompile_deltas (const HBUINT8 *&p /* IN/OUT */, hb_vector_t &deltas /* IN/OUT */, const HBUINT8 *end, - bool consume_all = false) + bool consume_all = false, + unsigned start = 0) { - return TupleValues::decompile (p, deltas, end, consume_all); + return TupleValues::decompile (p, deltas, end, consume_all, start); } bool has_data () const { return tupleVarCount; } @@ -1463,6 +1576,7 @@ struct TupleVariationData const hb_vector_t &shared_indices, const hb_array_t shared_tuples, tuple_variations_t& tuple_variations, /* OUT */ + hb_alloc_pool_t *pool = nullptr, bool is_composite_glyph = false) const { return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount, @@ -1470,6 +1584,7 @@ struct TupleVariationData axes_old_index_tag_map, shared_indices, shared_tuples, + pool, is_composite_glyph); } @@ -1634,8 +1749,9 @@ struct item_variations_t bool instantiate_tuple_vars (const hb_hashmap_t& normalized_axes_location, const hb_hashmap_t& axes_triple_distances) { + optimize_scratch_t scratch; for (tuple_variations_t& tuple_vars : vars) - if (!tuple_vars.instantiate (normalized_axes_location, axes_triple_distances)) + if (!tuple_vars.instantiate (normalized_axes_location, axes_triple_distances, scratch)) return false; if (!build_region_list ()) return false; @@ -1720,30 +1836,28 @@ struct item_variations_t struct combined_gain_idx_tuple_t { - int gain; - unsigned idx_1; - unsigned idx_2; + uint64_t encoded; combined_gain_idx_tuple_t () = default; - combined_gain_idx_tuple_t (int gain_, unsigned i, unsigned j) - :gain (gain_), idx_1 (i), idx_2 (j) {} + combined_gain_idx_tuple_t (unsigned gain, unsigned i, unsigned j) + : encoded ((uint64_t (0xFFFFFF - gain) << 40) | (uint64_t (i) << 20) | uint64_t (j)) + { + assert (gain < 0xFFFFFF); + assert (i < 0xFFFFFFF && j < 0xFFFFFFF); + } bool operator < (const combined_gain_idx_tuple_t& o) { - if (gain != o.gain) - return gain < o.gain; - - if (idx_1 != o.idx_1) - return idx_1 < o.idx_1; - - return idx_2 < o.idx_2; + return encoded < o.encoded; } bool operator <= (const combined_gain_idx_tuple_t& o) { - if (*this < o) return true; - return gain == o.gain && idx_1 == o.idx_1 && idx_2 == o.idx_2; + return encoded <= o.encoded; } + + unsigned idx_1 () const { return (encoded >> 20) & 0xFFFFF; }; + unsigned idx_2 () const { return encoded & 0xFFFFF; }; }; bool as_item_varstore (bool optimize=true, bool use_no_variation_idx=true) @@ -1765,9 +1879,9 @@ struct item_variations_t hb_hashmap_t*> front_mapping; unsigned start_row = 0; hb_vector_t encoding_objs; - hb_hashmap_t, unsigned> chars_idx_map; /* delta_rows map, used for filtering out duplicate rows */ + hb_vector_t *> major_rows; hb_hashmap_t*, unsigned> delta_rows_map; for (unsigned major = 0; major < vars.length; major++) { @@ -1775,6 +1889,9 @@ struct item_variations_t * (row based) delta */ const tuple_variations_t& tuples = vars[major]; unsigned num_rows = var_data_num_rows[major]; + + if (!num_rows) continue; + for (const tuple_delta_t& tuple: tuples.tuple_vars) { if (tuple.deltas_x.length != num_rows) @@ -1789,24 +1906,11 @@ struct item_variations_t { int rounded_delta = roundf (tuple.deltas_x[i]); delta_rows[start_row + i][*col_idx] += rounded_delta; - if ((!has_long) && (rounded_delta < -65536 || rounded_delta > 65535)) - has_long = true; + has_long |= rounded_delta < -65536 || rounded_delta > 65535; } } - if (!optimize) - { - /* assemble a delta_row_encoding_t for this subtable, skip optimization so - * chars is not initialized, we only need delta rows for serialization */ - delta_row_encoding_t obj; - for (unsigned r = start_row; r < start_row + num_rows; r++) - obj.add_row (&(delta_rows.arrayZ[r])); - - encodings.push (std::move (obj)); - start_row += num_rows; - continue; - } - + major_rows.reset (); for (unsigned minor = 0; minor < num_rows; minor++) { const hb_vector_t& row = delta_rows[start_row + minor]; @@ -1828,42 +1932,40 @@ struct item_variations_t if (!front_mapping.set ((major<<16) + minor, &row)) return false; - hb_vector_t chars = delta_row_encoding_t::get_row_chars (row); - if (!chars) return false; - if (delta_rows_map.has (&row)) continue; delta_rows_map.set (&row, 1); - unsigned *obj_idx; - if (chars_idx_map.has (chars, &obj_idx)) - { - delta_row_encoding_t& obj = encoding_objs[*obj_idx]; - if (!obj.add_row (&row)) - return false; - } - else - { - if (!chars_idx_map.set (chars, encoding_objs.length)) - return false; - delta_row_encoding_t obj (std::move (chars), &row); - encoding_objs.push (std::move (obj)); - } + + major_rows.push (&row); } + if (major_rows) + encoding_objs.push (delta_row_encoding_t (std::move (major_rows), num_cols)); + start_row += num_rows; } /* return directly if no optimization, maintain original VariationIndex so * varidx_map would be empty */ - if (!optimize) return !encodings.in_error (); + if (!optimize) + { + encodings = std::move (encoding_objs); + return !encodings.in_error (); + } - /* sort encoding_objs */ + /* NOTE: Fonttools instancer always optimizes VarStore from scratch. This + * is too costly for large fonts. So, instead, we retain the encodings of + * the original VarStore, and just try to combine them if possible. This + * is a compromise between optimization and performance and practically + * works very well. */ + + // This produces slightly smaller results in some cases. encoding_objs.qsort (); - /* main algorithm: repeatedly pick 2 best encodings to combine, and combine - * them */ - hb_priority_queue_t queue; + /* main algorithm: repeatedly pick 2 best encodings to combine, and combine them */ + using item_t = hb_priority_queue_t::item_t; + hb_vector_t queue_items; unsigned num_todos = encoding_objs.length; for (unsigned i = 0; i < num_todos; i++) { @@ -1871,16 +1973,18 @@ struct item_variations_t { int combining_gain = encoding_objs.arrayZ[i].gain_from_merging (encoding_objs.arrayZ[j]); if (combining_gain > 0) - queue.insert (combined_gain_idx_tuple_t (-combining_gain, i, j), 0); + queue_items.push (item_t (combined_gain_idx_tuple_t (combining_gain, i, j), 0)); } } - hb_set_t removed_todo_idxes; + hb_priority_queue_t queue (std::move (queue_items)); + + hb_bit_set_t removed_todo_idxes; while (queue) { auto t = queue.pop_minimum ().first; - unsigned i = t.idx_1; - unsigned j = t.idx_2; + unsigned i = t.idx_1 (); + unsigned j = t.idx_2 (); if (removed_todo_idxes.has (i) || removed_todo_idxes.has (j)) continue; @@ -1891,40 +1995,36 @@ struct item_variations_t removed_todo_idxes.add (i); removed_todo_idxes.add (j); - hb_vector_t combined_chars; - if (!combined_chars.alloc (encoding.chars.length)) - return false; - - for (unsigned idx = 0; idx < encoding.chars.length; idx++) - { - uint8_t v = hb_max (encoding.chars.arrayZ[idx], other_encoding.chars.arrayZ[idx]); - combined_chars.push (v); - } - - delta_row_encoding_t combined_encoding_obj (std::move (combined_chars)); - for (const auto& row : hb_concat (encoding.items, other_encoding.items)) - combined_encoding_obj.add_row (row); + encoding.merge (other_encoding); for (unsigned idx = 0; idx < encoding_objs.length; idx++) { if (removed_todo_idxes.has (idx)) continue; const delta_row_encoding_t& obj = encoding_objs.arrayZ[idx]; - if (obj.chars == combined_chars) + // In the unlikely event that the same encoding exists already, combine it. + if (obj.width == encoding.width && obj.chars == encoding.chars) { + // This is straight port from fonttools algorithm. I added this branch there + // because I thought it can happen. But looks like we never get in here in + // practice. I'm not confident enough to remove it though; in theory it can + // happen. I think it's just that our tests are not extensive enough to hit + // this path. + for (const auto& row : obj.items) - combined_encoding_obj.add_row (row); + encoding.add_row (row); removed_todo_idxes.add (idx); continue; } - int combined_gain = combined_encoding_obj.gain_from_merging (obj); + int combined_gain = encoding.gain_from_merging (obj); if (combined_gain > 0) - queue.insert (combined_gain_idx_tuple_t (-combined_gain, idx, encoding_objs.length), 0); + queue.insert (combined_gain_idx_tuple_t (combined_gain, idx, encoding_objs.length), 0); } - encoding_objs.push (std::move (combined_encoding_obj)); + auto moved_encoding = std::move (encoding); + encoding_objs.push (moved_encoding); } int num_final_encodings = (int) encoding_objs.length - (int) removed_todo_idxes.get_population (); @@ -1937,9 +2037,6 @@ struct item_variations_t encodings.push (std::move (encoding_objs.arrayZ[i])); } - /* sort again based on width, make result deterministic */ - encodings.qsort (delta_row_encoding_t::cmp_width); - return compile_varidx_map (front_mapping); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh index 3dc4ebaebd8..d8f497d5346 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-cvar-table.hh @@ -84,7 +84,7 @@ struct cvar if (!coords) return true; hb_vector_t shared_indices; TupleVariationData<>::tuple_iterator_t iterator; - unsigned var_data_length = tuple_var_data->get_size (axis_count); + unsigned var_data_length = tuple_var_data->get_size (axis_count * 2); hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast (tuple_var_data), var_data_length); if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, base, shared_indices, &iterator)) @@ -113,7 +113,7 @@ struct cvar bool apply_to_all = (indices.length == 0); unsigned num_deltas = apply_to_all ? num_cvt_item : indices.length; - if (unlikely (!unpacked_deltas.resize (num_deltas, false))) return false; + if (unlikely (!unpacked_deltas.resize_dirty (num_deltas))) return false; if (unlikely (!TupleVariationData<>::decompile_deltas (p, unpacked_deltas, end))) return false; for (unsigned int i = 0; i < num_deltas; i++) @@ -158,7 +158,8 @@ struct cvar tuple_variations)) return_trace (false); - if (!tuple_variations.instantiate (c->plan->axes_location, c->plan->axes_triple_distances)) + optimize_scratch_t scratch; + if (!tuple_variations.instantiate (c->plan->axes_location, c->plan->axes_triple_distances, scratch)) return_trace (false); if (!tuple_variations.compile_bytes (c->plan->axes_index_map, c->plan->axes_old_index_tag_map, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh index f2725eaa282..74d392de9b7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh @@ -179,7 +179,7 @@ struct AxisRecord hb_tag_t get_axis_tag () const { return axisTag; } - int normalize_axis_value (float v) const + float normalize_axis_value (float v) const { float min_value, default_value, max_value; get_coordinates (min_value, default_value, max_value); @@ -189,23 +189,9 @@ struct AxisRecord if (v == default_value) return 0; else if (v < default_value) - v = (v - default_value) / (default_value - min_value); + return (v - default_value) / (default_value - min_value); else - v = (v - default_value) / (max_value - default_value); - return roundf (v * 16384.f); - } - - float unnormalize_axis_value (int v) const - { - float min_value, default_value, max_value; - get_coordinates (min_value, default_value, max_value); - - if (v == 0) - return default_value; - else if (v < 0) - return v * (default_value - min_value) / 16384.f + default_value; - else - return v * (max_value - default_value) / 16384.f + default_value; + return (v - default_value) / (max_value - default_value); } hb_ot_name_id_t get_name_id () const { return axisNameID; } @@ -341,12 +327,9 @@ struct fvar return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true); } - int normalize_axis_value (unsigned int axis_index, float v) const + float normalize_axis_value (unsigned int axis_index, float v) const { return get_axes ()[axis_index].normalize_axis_value (v); } - float unnormalize_axis_value (unsigned int axis_index, int v) const - { return get_axes ()[axis_index].unnormalize_axis_value (v); } - unsigned int get_instance_count () const { return instanceCount; } hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh index 9f9b5be3cbe..7c42f548456 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh @@ -66,12 +66,14 @@ struct glyph_variations_t hb_vector_t glyph_variations; - hb_vector_t compiled_shared_tuples; + hb_vector_t compiled_shared_tuples; private: unsigned shared_tuples_count = 0; /* shared coords-> index map after instantiation */ - hb_hashmap_t*, unsigned> shared_tuples_idx_map; + hb_hashmap_t*, unsigned> shared_tuples_idx_map; + + hb_alloc_pool_t pool; public: unsigned compiled_shared_tuples_count () const @@ -128,6 +130,7 @@ struct glyph_variations_t iterator, &(plan->axes_old_index_tag_map), shared_indices, shared_tuples, tuple_vars, /* OUT */ + &pool, is_composite_glyph)) return false; glyph_variations.push (std::move (tuple_vars)); @@ -139,6 +142,7 @@ struct glyph_variations_t { unsigned count = plan->new_to_old_gid_list.length; bool iup_optimize = false; + optimize_scratch_t scratch; iup_optimize = plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS; for (unsigned i = 0; i < count; i++) { @@ -146,7 +150,7 @@ struct glyph_variations_t contour_point_vector_t *all_points; if (!plan->new_gid_contour_points_map.has (new_gid, &all_points)) return false; - if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points, iup_optimize)) + if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, scratch, &pool, all_points, iup_optimize)) return false; } return true; @@ -161,7 +165,8 @@ struct glyph_variations_t if (!vars.compile_bytes (axes_index_map, axes_old_index_tag_map, true, /* use shared points*/ true, - &shared_tuples_idx_map)) + &shared_tuples_idx_map, + &pool)) return false; return true; @@ -172,20 +177,21 @@ struct glyph_variations_t { /* key is pointer to compiled_peak_coords inside each tuple, hashing * function will always deref pointers first */ - hb_hashmap_t*, unsigned> coords_count_map; + hb_hashmap_t*, unsigned> coords_count_map; /* count the num of shared coords */ for (tuple_variations_t& vars: glyph_variations) { for (tuple_delta_t& var : vars.tuple_vars) { - if (!var.compile_peak_coords (axes_index_map, axes_old_index_tag_map)) + if (!var.compile_coords (axes_index_map, axes_old_index_tag_map, &pool)) return false; - unsigned* count; - if (coords_count_map.has (&(var.compiled_peak_coords), &count)) - coords_count_map.set (&(var.compiled_peak_coords), *count + 1); + unsigned *count; + unsigned hash = hb_hash (&var.compiled_peak_coords); + if (coords_count_map.has_with_hash (&(var.compiled_peak_coords), hash, &count)) + (*count)++; else - coords_count_map.set (&(var.compiled_peak_coords), 1); + coords_count_map.set_with_hash (&(var.compiled_peak_coords), hash, 1); } } @@ -193,66 +199,45 @@ struct glyph_variations_t return false; /* add only those coords that are used more than once into the vector and sort */ - hb_vector_t*> shared_coords; - if (unlikely (!shared_coords.alloc (coords_count_map.get_population ()))) - return false; - - for (const auto _ : coords_count_map.iter ()) - { - if (_.second == 1) continue; - shared_coords.push (_.first); - } + hb_vector_t*, unsigned>> shared_coords { + + hb_iter (coords_count_map) + | hb_filter ([] (const hb_pair_t*, unsigned>& p) { return p.second > 1; }) + }; + if (unlikely (shared_coords.in_error ())) return false; /* no shared tuples: no coords are used more than once */ if (!shared_coords) return true; /* sorting based on the coords frequency first (high to low), then compare * the coords bytes */ - hb_qsort (shared_coords.arrayZ, shared_coords.length, sizeof (hb_vector_t*), _cmp_coords, (void *) (&coords_count_map)); + shared_coords.qsort (_cmp_coords); /* build shared_coords->idx map and shared tuples byte array */ shared_tuples_count = hb_min (0xFFFu + 1, shared_coords.length); - unsigned len = shared_tuples_count * (shared_coords[0]->length); + unsigned len = shared_tuples_count * (shared_coords[0].first->length); if (unlikely (!compiled_shared_tuples.alloc (len))) return false; for (unsigned i = 0; i < shared_tuples_count; i++) { - shared_tuples_idx_map.set (shared_coords[i], i); + shared_tuples_idx_map.set (shared_coords[i].first, i); /* add a concat() in hb_vector_t? */ - for (char c : shared_coords[i]->iter ()) + for (auto c : shared_coords[i].first->iter ()) compiled_shared_tuples.push (c); } return true; } - static int _cmp_coords (const void *pa, const void *pb, void *arg) + static int _cmp_coords (const void *pa, const void *pb) { - const hb_hashmap_t*, unsigned>* coords_count_map = - reinterpret_cast*, unsigned>*> (arg); + const hb_pair_t *, unsigned> *a = (const hb_pair_t *, unsigned> *) pa; + const hb_pair_t *, unsigned> *b = (const hb_pair_t *, unsigned> *) pb; - /* shared_coords is hb_vector_t*> so casting pa/pb - * to be a pointer to a pointer */ - const hb_vector_t** a = reinterpret_cast**> (const_cast(pa)); - const hb_vector_t** b = reinterpret_cast**> (const_cast(pb)); + if (a->second != b->second) + return b->second - a->second; // high to low - bool has_a = coords_count_map->has (*a); - bool has_b = coords_count_map->has (*b); - - if (has_a && has_b) - { - unsigned a_num = coords_count_map->get (*a); - unsigned b_num = coords_count_map->get (*b); - - if (a_num != b_num) - return b_num - a_num; - - return (*b)->as_array().cmp ((*a)->as_array ()); - } - else if (has_a) return -1; - else if (has_b) return 1; - else return 0; + return b->first->as_array().cmp (a->first->as_array ()); } templatesharedTuples = 0; else { - hb_array_t shared_tuples = glyph_vars.compiled_shared_tuples.as_array ().copy (c); + hb_array_t shared_tuples = glyph_vars.compiled_shared_tuples.as_array ().copy (c); if (!shared_tuples.arrayZ) return_trace (false); - out->sharedTuples = shared_tuples.arrayZ - (char *) out; + out->sharedTuples = (const char *) shared_tuples.arrayZ - (char *) out; } char *glyph_var_data = c->start_embed (); @@ -463,10 +448,18 @@ struct gvar_GVAR if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) it++; unsigned int subset_data_size = 0; + unsigned padding_size = 0; for (auto &_ : it) { hb_codepoint_t old_gid = _.second; - subset_data_size += get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length; + unsigned glyph_data_size = get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length; + if (glyph_data_size % 2) + { + glyph_data_size++; + padding_size++; + } + + subset_data_size += glyph_data_size; } /* According to the spec: If the short format (Offset16) is used for offsets, @@ -495,6 +488,8 @@ struct gvar_GVAR /* This ordering relative to the shared tuples array, which puts the glyphVariationData last in the table, is required when HB_SUBSET_FLAGS_IFTB_REQUIREMENTS is set */ + if (long_offset) + subset_data_size -= padding_size; char *subset_data = c->serializer->allocate_size (subset_data_size, false); if (!subset_data) return_trace (false); out->dataZ = subset_data - (char *) out; @@ -533,8 +528,16 @@ struct gvar_GVAR old_gid); hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length); - subset_data += var_data_bytes.length; - glyph_offset += var_data_bytes.length; + unsigned glyph_data_size = var_data_bytes.length; + subset_data += glyph_data_size; + glyph_offset += glyph_data_size; + + if (!long_offset && (glyph_data_size % 2)) + { + *subset_data = 0; + subset_data++; + glyph_offset++; + } if (long_offset) ((HBUINT32 *) subset_offsets)[gid] = glyph_offset; @@ -582,6 +585,17 @@ struct gvar_GVAR public: struct accelerator_t { + + hb_scalar_cache_t *create_cache () const + { + return hb_scalar_cache_t::create (table->sharedTupleCount); + } + + static void destroy_cache (hb_scalar_cache_t *cache) + { + hb_scalar_cache_t::destroy (cache); + } + bool has_data () const { return table->has_data (); } accelerator_t (hb_face_t *face) @@ -589,36 +603,6 @@ struct gvar_GVAR table = hb_sanitize_context_t ().reference_table (face); /* If sanitize failed, set glyphCount to 0. */ glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0; - - /* For shared tuples that only have one or two axes active, shared the index - * of that axis as a cache. This will speed up caclulate_scalar() a lot - * for fonts with lots of axes and many "monovar" or "duovar" tuples. */ - hb_array_t shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); - unsigned count = table->sharedTupleCount; - if (unlikely (!shared_tuple_active_idx.resize (count, false))) return; - unsigned axis_count = table->axisCount; - for (unsigned i = 0; i < count; i++) - { - hb_array_t tuple = shared_tuples.sub_array (axis_count * i, axis_count); - int idx1 = -1, idx2 = -1; - for (unsigned j = 0; j < axis_count; j++) - { - const F2DOT14 &peak = tuple.arrayZ[j]; - if (peak.to_int () != 0) - { - if (idx1 == -1) - idx1 = j; - else if (idx2 == -1) - idx2 = j; - else - { - idx1 = idx2 = -1; - break; - } - } - } - shared_tuple_active_idx.arrayZ[i] = {idx1, idx2}; - } } ~accelerator_t () { table.destroy (); } @@ -655,6 +639,7 @@ struct gvar_GVAR hb_array_t coords, const hb_array_t points, hb_glyf_scratch_t &scratch, + hb_scalar_cache_t *gvar_cache = nullptr, bool phantom_only = false) const { if (unlikely (glyph >= glyphCount)) return true; @@ -690,10 +675,12 @@ struct gvar_GVAR unsigned count = points.length; bool flush = false; + do { float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples, - &shared_tuple_active_idx); + gvar_cache); + if (scalar == 0.f) continue; const HBUINT8 *p = iterator.get_serialized_data (); unsigned int length = iterator.current_tuple->get_data_size (); @@ -702,7 +689,7 @@ struct gvar_GVAR if (!deltas) { - if (unlikely (!deltas_vec.resize (count, false))) return false; + if (unlikely (!deltas_vec.resize_dirty (count))) return false; deltas = deltas_vec.as_array (); hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0, (phantom_only ? 4 : count) * sizeof (deltas[0])); @@ -717,11 +704,12 @@ struct gvar_GVAR const hb_array_t &indices = has_private_points ? private_indices : shared_indices; bool apply_to_all = (indices.length == 0); - unsigned int num_deltas = apply_to_all ? points.length : indices.length; - if (unlikely (!x_deltas.resize (num_deltas, false))) return false; - if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end))) return false; - if (unlikely (!y_deltas.resize (num_deltas, false))) return false; - if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end))) return false; + unsigned num_deltas = apply_to_all ? points.length : indices.length; + unsigned start_deltas = (apply_to_all && phantom_only && num_deltas >= 4 ? num_deltas - 4 : 0); + if (unlikely (!x_deltas.resize_dirty (num_deltas))) return false; + if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end, false, start_deltas))) return false; + if (unlikely (!y_deltas.resize_dirty (num_deltas))) return false; + if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end, false, start_deltas))) return false; if (!apply_to_all) { @@ -884,7 +872,6 @@ struct gvar_GVAR private: hb_blob_ptr_t table; unsigned glyphCount; - hb_vector_t> shared_tuple_active_idx; }; protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh index 40a85a62b78..652d738e7b5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh @@ -42,57 +42,62 @@ struct index_map_subset_plan_t VORG_INDEX }; - void init (const DeltaSetIndexMap &index_map, + void init (const DeltaSetIndexMap *index_map, hb_inc_bimap_t &outer_map, hb_vector_t &inner_sets, const hb_subset_plan_t *plan, bool bypass_empty = true) { map_count = 0; - outer_bit_count = 0; - inner_bit_count = 1; max_inners.init (); output_map.init (); - if (bypass_empty && !index_map.get_map_count ()) return; + if (bypass_empty && (!index_map || !index_map->get_map_count ())) return; unsigned int last_val = (unsigned int)-1; hb_codepoint_t last_gid = HB_CODEPOINT_INVALID; - outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count (); max_inners.resize (inner_sets.length); for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0; /* Search backwards for a map value different from the last map value */ auto &new_to_old_gid_list = plan->new_to_old_gid_list; unsigned count = new_to_old_gid_list.length; - for (unsigned j = count; j; j--) + if (!index_map) { - hb_codepoint_t gid = new_to_old_gid_list.arrayZ[j - 1].first; - hb_codepoint_t old_gid = new_to_old_gid_list.arrayZ[j - 1].second; - - unsigned int v = index_map.map (old_gid); - if (last_gid == HB_CODEPOINT_INVALID) + map_count = new_to_old_gid_list.tail ().first + 1; + } + else + { + for (unsigned j = count; j; j--) { + hb_codepoint_t gid = new_to_old_gid_list.arrayZ[j - 1].first; + hb_codepoint_t old_gid = new_to_old_gid_list.arrayZ[j - 1].second; + + unsigned int v = index_map->map (old_gid); + if (last_gid == HB_CODEPOINT_INVALID) + { last_val = v; last_gid = gid; continue; - } - if (v != last_val) + } + if (v != last_val) break; - last_gid = gid; + last_gid = gid; + } + + if (unlikely (last_gid == (hb_codepoint_t)-1)) return; + map_count = last_gid + 1; } - if (unlikely (last_gid == (hb_codepoint_t)-1)) return; - map_count = last_gid + 1; for (auto _ : plan->new_to_old_gid_list) { hb_codepoint_t gid = _.first; if (gid >= map_count) break; hb_codepoint_t old_gid = _.second; - unsigned int v = index_map.map (old_gid); + unsigned int v = index_map ? index_map->map (old_gid): old_gid; unsigned int outer = v >> 16; unsigned int inner = v & 0xFFFF; outer_map.add (outer); @@ -113,6 +118,9 @@ struct index_map_subset_plan_t const hb_vector_t &inner_maps, const hb_subset_plan_t *plan) { + outer_bit_count = 1; + inner_bit_count = 1; + for (unsigned int i = 0; i < max_inners.length; i++) { if (inner_maps[i].get_population () == 0) continue; @@ -128,9 +136,13 @@ struct index_map_subset_plan_t if (unlikely (new_gid >= map_count)) break; - uint32_t v = input_map->map (old_gid); - unsigned int outer = v >> 16; - output_map.arrayZ[new_gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]); + uint32_t v = input_map? input_map->map (old_gid) : old_gid; + unsigned outer = v >> 16; + unsigned new_outer = outer_map[outer]; + unsigned bit_count = (new_outer == 0) ? 1 : hb_bit_storage (new_outer); + outer_bit_count = hb_max (bit_count, outer_bit_count); + + output_map.arrayZ[new_gid] = (new_outer << 16) | (inner_maps[outer][v & 0xFFFF]); } } @@ -204,8 +216,8 @@ struct hvarvvar_subset_plan_t if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return; bool retain_adv_map = false; - index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan, false); - if (index_maps[0] == &Null (DeltaSetIndexMap)) + index_map_plans[0].init (index_maps[0], outer_map, inner_sets, plan, false); + if (!index_maps[0]) { retain_adv_map = plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS; outer_map.add (0); @@ -215,7 +227,7 @@ struct hvarvvar_subset_plan_t } for (unsigned int i = 1; i < index_maps.length; i++) - index_map_plans[i].init (*index_maps[i], outer_map, inner_sets, plan); + index_map_plans[i].init (index_maps[i], outer_map, inner_sets, plan); outer_map.sort (); @@ -284,6 +296,8 @@ struct HVARVVAR static constexpr hb_tag_t HVARTag = HB_OT_TAG_HVAR; static constexpr hb_tag_t VVARTag = HB_OT_TAG_VVAR; + bool has_data () const { return version.major != 0; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -301,9 +315,14 @@ struct HVARVVAR void listup_index_maps (hb_vector_t &index_maps) const { - index_maps.push (&(this+advMap)); - index_maps.push (&(this+lsbMap)); - index_maps.push (&(this+rsbMap)); + if (advMap) index_maps.push (&(this+advMap)); + else index_maps.push (nullptr); + + if (lsbMap) index_maps.push (&(this+lsbMap)); + else index_maps.push (nullptr); + + if (rsbMap) index_maps.push (&(this+rsbMap)); + else index_maps.push (nullptr); } bool serialize_index_maps (hb_serialize_context_t *c, @@ -382,9 +401,10 @@ struct HVARVVAR hvar_plan.index_map_plans.as_array ())); } + HB_ALWAYS_INLINE float get_advance_delta_unscaled (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - ItemVariationStore::cache_t *store_cache = nullptr) const + hb_scalar_cache_t *store_cache = nullptr) const { uint32_t varidx = (this+advMap).map (glyph); return (this+varStore).get_delta (varidx, @@ -392,16 +412,6 @@ struct HVARVVAR store_cache); } - bool get_lsb_delta_unscaled (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count, - float *lsb) const - { - if (!lsbMap) return false; - uint32_t varidx = (this+lsbMap).map (glyph); - *lsb = (this+varStore).get_delta (varidx, coords, coord_count); - return true; - } - public: FixedVersion<>version; /* Version of the metrics variation table * initially set to 0x00010000u */ @@ -435,7 +445,8 @@ struct VVAR : HVARVVAR { void listup_index_maps (hb_vector_t &index_maps) const { HVARVVAR::listup_index_maps (index_maps); - index_maps.push (&(this+vorgMap)); + if (vorgMap) index_maps.push (&(this+vorgMap)); + else index_maps.push (nullptr); } bool serialize_index_maps (hb_serialize_context_t *c, @@ -454,14 +465,16 @@ struct VVAR : HVARVVAR { bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } - bool get_vorg_delta_unscaled (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count, - float *delta) const + HB_ALWAYS_INLINE + float get_vorg_delta_unscaled (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + hb_scalar_cache_t *store_cache = nullptr) const { - if (!vorgMap) return false; + if (!vorgMap) return 0.f; uint32_t varidx = (this+vorgMap).map (glyph); - *delta = (this+varStore).get_delta (varidx, coords, coord_count); - return true; + return (this+varStore).get_delta (varidx, + coords, coord_count, + store_cache); } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc index 8c695c41e24..4d594d587d8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc @@ -286,10 +286,14 @@ hb_ot_var_normalize_variations (hb_face_t *face, hb_ot_var_axis_info_t info; if (hb_ot_var_find_axis_info (face, variations[i].tag, &info) && info.axis_index < coords_length) - coords[info.axis_index] = fvar.normalize_axis_value (info.axis_index, variations[i].value); + coords[info.axis_index] = roundf (fvar.normalize_axis_value (info.axis_index, variations[i].value) * 65536.0f); } - face->table.avar->map_coords (coords, coords_length); + face->table.avar->map_coords_16_16 (coords, coords_length); + + // Round to 2.14 + for (unsigned i = 0; i < coords_length; i++) + coords[i] = (coords[i] + 2) >> 2; } /** @@ -309,6 +313,10 @@ hb_ot_var_normalize_variations (hb_face_t *face, * Any additional scaling defined in the face's `avar` table is also * applied, as described at https://docs.microsoft.com/en-us/typography/opentype/spec/avar * + * Note: @coords_length must be the same as the number of axes in the face, as + * for example returned by hb_ot_var_get_axis_count(). + * Otherwise, the behavior is undefined. + * * Since: 1.4.2 **/ void @@ -319,9 +327,13 @@ hb_ot_var_normalize_coords (hb_face_t *face, { const OT::fvar &fvar = *face->table.fvar; for (unsigned int i = 0; i < coords_length; i++) - normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]); + normalized_coords[i] = roundf (fvar.normalize_axis_value (i, design_coords[i]) * 65536.0f); - face->table.avar->map_coords (normalized_coords, coords_length); + face->table.avar->map_coords_16_16 (normalized_coords, coords_length); + + // Round to 2.14 + for (unsigned i = 0; i < coords_length; i++) + normalized_coords[i] = (normalized_coords[i] + 2) >> 2; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh index b2300a327da..ff7ce678e4e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh @@ -61,6 +61,7 @@ struct VORG bool has_data () const { return version.to_int (); } + HB_ALWAYS_INLINE int get_y_origin (hb_codepoint_t glyph) const { unsigned int i; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-outline.cc b/src/java.desktop/share/native/libharfbuzz/hb-outline.cc index 85ffc793678..12963fe46e3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-outline.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-outline.cc @@ -84,6 +84,15 @@ void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const } } +void hb_outline_t::translate (float dx, float dy) +{ + for (auto &p : points) + { + p.x += dx; + p.y += dy; + } +} + void hb_outline_t::slant (float slant_xy) { for (auto &p : points) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-outline.hh b/src/java.desktop/share/native/libharfbuzz/hb-outline.hh index c5b68c05dd7..9e394a68d9c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-outline.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-outline.hh @@ -69,6 +69,7 @@ struct hb_outline_t HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; HB_INTERNAL float control_area () const; + HB_INTERNAL void translate (float dx, float dy); HB_INTERNAL void slant (float slant_xy); HB_INTERNAL void embolden (float x_strength, float y_strength, float x_shift, float y_shift); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc index bc08c5a9e14..e867f37f3c5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.cc @@ -49,7 +49,7 @@ hb_paint_extents_push_transform (hb_paint_funcs_t *funcs HB_UNUSED, { hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; - c->push_transform (hb_transform_t {xx, yx, xy, yy, dx, dy}); + c->push_transform (hb_transform_t<> {xx, yx, xy, yy, dx, dy}); } static void @@ -71,7 +71,7 @@ hb_paint_extents_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED, { hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; - hb_extents_t extents; + hb_extents_t<> extents; hb_draw_funcs_t *draw_extent_funcs = hb_draw_extents_get_funcs (); hb_font_draw_glyph (font, glyph, draw_extent_funcs, &extents); c->push_clip (extents); @@ -85,7 +85,7 @@ hb_paint_extents_push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED, { hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; - hb_extents_t extents = {xmin, ymin, xmax, ymax}; + hb_extents_t<> extents = {xmin, ymin, xmax, ymax}; c->push_clip (extents); } @@ -136,10 +136,10 @@ hb_paint_extents_paint_image (hb_paint_funcs_t *funcs HB_UNUSED, if (!glyph_extents) return false; // Happens with SVG images. - hb_extents_t extents = {(float) glyph_extents->x_bearing, - (float) glyph_extents->y_bearing + glyph_extents->height, - (float) glyph_extents->x_bearing + glyph_extents->width, - (float) glyph_extents->y_bearing}; + hb_extents_t<> extents = {(float) glyph_extents->x_bearing, + (float) glyph_extents->y_bearing + glyph_extents->height, + (float) glyph_extents->x_bearing + glyph_extents->width, + (float) glyph_extents->y_bearing}; c->push_clip (extents); c->paint (); c->pop_clip (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh index 60374eaa2d2..531eb3c7c52 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint-extents.hh @@ -41,9 +41,9 @@ struct hb_paint_extents_context_t clips.clear (); groups.clear (); - transforms.push (hb_transform_t{}); - clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED}); - groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); + transforms.push (hb_transform_t<>{}); + clips.push (hb_bounds_t<>{hb_bounds_t<>::UNBOUNDED}); + groups.push (hb_bounds_t<>{hb_bounds_t<>::EMPTY}); } hb_paint_extents_context_t () @@ -51,19 +51,19 @@ struct hb_paint_extents_context_t clear (); } - hb_extents_t get_extents () + hb_extents_t<> get_extents () { return groups.tail().extents; } bool is_bounded () { - return groups.tail().status != hb_bounds_t::UNBOUNDED; + return groups.tail().status != hb_bounds_t<>::UNBOUNDED; } - void push_transform (const hb_transform_t &trans) + void push_transform (const hb_transform_t<> &trans) { - hb_transform_t t = transforms.tail (); + hb_transform_t<> t = transforms.tail (); t.multiply (trans); transforms.push (t); } @@ -73,13 +73,13 @@ struct hb_paint_extents_context_t transforms.pop (); } - void push_clip (hb_extents_t extents) + void push_clip (hb_extents_t<> extents) { /* Transform extents and push a new clip. */ - const hb_transform_t &t = transforms.tail (); + const hb_transform_t<> &t = transforms.tail (); t.transform_extents (extents); - auto bounds = hb_bounds_t {extents}; + auto bounds = hb_bounds_t<> {extents}; bounds.intersect (clips.tail ()); clips.push (bounds); @@ -92,19 +92,19 @@ struct hb_paint_extents_context_t void push_group () { - groups.push (hb_bounds_t {hb_bounds_t::EMPTY}); + groups.push (hb_bounds_t<> {hb_bounds_t<>::EMPTY}); } void pop_group (hb_paint_composite_mode_t mode) { - const hb_bounds_t src_bounds = groups.pop (); - hb_bounds_t &backdrop_bounds = groups.tail (); + const hb_bounds_t<> src_bounds = groups.pop (); + hb_bounds_t<> &backdrop_bounds = groups.tail (); // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite switch ((int) mode) { case HB_PAINT_COMPOSITE_MODE_CLEAR: - backdrop_bounds.status = hb_bounds_t::EMPTY; + backdrop_bounds.status = hb_bounds_t<>::EMPTY; break; case HB_PAINT_COMPOSITE_MODE_SRC: case HB_PAINT_COMPOSITE_MODE_SRC_OUT: @@ -125,16 +125,16 @@ struct hb_paint_extents_context_t void paint () { - const hb_bounds_t &clip = clips.tail (); - hb_bounds_t &group = groups.tail (); + const hb_bounds_t<> &clip = clips.tail (); + hb_bounds_t<> &group = groups.tail (); group.union_ (clip); } protected: - hb_vector_t transforms; - hb_vector_t clips; - hb_vector_t groups; + hb_vector_t> transforms; + hb_vector_t> clips; + hb_vector_t> groups; }; HB_INTERNAL hb_paint_funcs_t * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-paint.hh b/src/java.desktop/share/native/libharfbuzz/hb-paint.hh index 2abadebab3c..aedbcbdcbe2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-paint.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-paint.hh @@ -28,6 +28,7 @@ #include "hb.hh" #include "hb-face.hh" #include "hb-font.hh" +#include "hb-geometry.hh" #define HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS \ HB_PAINT_FUNC_IMPLEMENT (push_transform) \ @@ -72,7 +73,11 @@ struct hb_paint_funcs_t float xx, float yx, float xy, float yy, float dx, float dy) - { func.push_transform (this, paint_data, + { + // Handle -0.f to avoid -0.f == 0.f in the transform matrix. + if (dx == -0.f) dx = 0.f; + if (dy == -0.f) dy = 0.f; + func.push_transform (this, paint_data, xx, yx, xy, yy, dx, dy, !user_data ? nullptr : user_data->push_transform); } void pop_transform (void *paint_data) @@ -182,54 +187,59 @@ struct hb_paint_funcs_t 0, 0); } - HB_NODISCARD - bool push_translate (void *paint_data, + void push_transform (void *paint_data, hb_transform_t t) + { + push_transform (paint_data, t.xx, t.yx, t.xy, t.yy, t.x0, t.y0); + } + + void push_translate (void *paint_data, float dx, float dy) { - if (!dx && !dy) - return false; - push_transform (paint_data, - 1.f, 0.f, 0.f, 1.f, dx, dy); - return true; + hb_transform_t::translation (dx, dy)); } - HB_NODISCARD - bool push_scale (void *paint_data, + void push_scale (void *paint_data, float sx, float sy) { - if (sx == 1.f && sy == 1.f) - return false; - push_transform (paint_data, - sx, 0.f, 0.f, sy, 0.f, 0.f); - return true; + hb_transform_t::scaling (sx, sy)); + } + void push_scale_around_center (void *paint_data, + float sx, float sy, + float cx, float cy) + { + push_transform (paint_data, + hb_transform_t::scaling_around_center (sx, sy, cx, cy)); } - HB_NODISCARD - bool push_rotate (void *paint_data, + void push_rotate (void *paint_data, float a) { - if (!a) - return false; - - float cc = cosf (a * HB_PI); - float ss = sinf (a * HB_PI); - push_transform (paint_data, cc, ss, -ss, cc, 0.f, 0.f); - return true; + push_transform (paint_data, + hb_transform_t::rotation (a * HB_PI)); } - HB_NODISCARD - bool push_skew (void *paint_data, + void push_rotate_around_center (void *paint_data, + float a, + float cx, float cy) + { + push_transform (paint_data, + hb_transform_t::rotation_around_center (a * HB_PI, cx, cy)); + } + + void push_skew (void *paint_data, float sx, float sy) { - if (!sx && !sy) - return false; - - float x = tanf (-sx * HB_PI); - float y = tanf (+sy * HB_PI); - push_transform (paint_data, 1.f, y, x, 1.f, 0.f, 0.f); - return true; + push_transform (paint_data, + hb_transform_t::skewing (-sx * HB_PI, sy * HB_PI)); + } + void push_skew_around_center (void *paint_data, + float sx, float sy, + float cx, float cy) + { + push_transform (paint_data, + hb_transform_t::skewing_around_center (-sx * HB_PI, sy * HB_PI, cx, cy)); } }; DECLARE_NULL_INSTANCE (hb_paint_funcs_t); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh b/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh index 274d5df4c54..753e86b8ca6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-priority-queue.hh @@ -45,12 +45,22 @@ template struct hb_priority_queue_t { - private: + public: typedef hb_pair_t item_t; + + private: hb_vector_t heap; public: + hb_priority_queue_t () = default; + hb_priority_queue_t (hb_vector_t&& other) : heap (std::move (other)) + { + // Heapify the vector. + for (int i = (heap.length / 2) - 1; i >= 0; i--) + bubble_down (i); + } + void reset () { heap.resize (0); } bool in_error () const { return heap.in_error (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh b/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh index cb4fdeead2e..7d118b52169 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-repacker.hh @@ -217,11 +217,17 @@ bool _try_isolating_subgraphs (const hb_vector_t& over unsigned maximum_to_move = hb_max ((sorted_graph.num_roots_for_space (space) / 2u), 1u); if (roots_to_isolate.get_population () > maximum_to_move) { // Only move at most half of the roots in a space at a time. - unsigned extra = roots_to_isolate.get_population () - maximum_to_move; - while (extra--) { - uint32_t root = HB_SET_VALUE_INVALID; - roots_to_isolate.previous (&root); - roots_to_isolate.del (root); + // + // Note: this was ported from non-stable ids to stable ids. So to retain the same behaviour + // with regards to which roots are removed from the set we need to remove them in the topological + // order, not the object id order. + int extra = roots_to_isolate.get_population () - maximum_to_move; + for (unsigned id : sorted_graph.ordering_) { + if (!extra) break; + if (roots_to_isolate.has(id)) { + roots_to_isolate.del(id); + extra--; + } } } @@ -266,7 +272,7 @@ bool _resolve_shared_overflow(const hb_vector_t& overf result = sorted_graph.duplicate(&parents, r.child); } - if (result == (unsigned) -1) return result; + if (result == (unsigned) -1) return false; if (parents.get_population() > 1) { // If the duplicated node has more than one parent pre-emptively raise it's priority to the maximum. @@ -283,7 +289,7 @@ bool _resolve_shared_overflow(const hb_vector_t& overf sorted_graph.vertices_[result].give_max_priority(); } - return result; + return true; } static inline @@ -302,8 +308,11 @@ bool _process_overflows (const hb_vector_t& overflows, { // The child object is shared, we may be able to eliminate the overflow // by duplicating it. - if (!_resolve_shared_overflow(overflows, i, sorted_graph)) continue; - return true; + if (_resolve_shared_overflow(overflows, i, sorted_graph)) + return true; + + // Sometimes we can't duplicate a node which looks shared because it's not actually shared + // (eg. all links from the same parent) in this case continue on to other resolution options. } if (child.is_leaf () && !priority_bumped_parents.has (r.parent)) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh index 1f8ba32bae2..1e9ee9ba960 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh @@ -64,9 +64,6 @@ * * - Cast blob content to T*, call sanitize() method of it, * - If sanitize succeeded, return blob. - * - Otherwise, if blob is not writable, try making it writable, - * or copy if cannot be made writable in-place, - * - Call sanitize() again. Return blob if sanitize succeeded. * - Return empty blob otherwise. * * @@ -98,6 +95,12 @@ * structure is so complicated that by checking all offsets at sanitize() time, * we make the code much simpler in other methods, as offsets and referenced * objects do not need to be validated at each use site. + * + * Note: + * Sanitize was named so because it used to try to recover from errors by + * modifying the data to make it valid. This is no longer the case, as it + * could make HarfBuzz hallucinate new rules if there was aliasing in the + * data. However, the name stuck. See: https://behdad.github.io/harfbust/ */ /* This limits sanitizing time on really broken fonts. */ @@ -120,12 +123,12 @@ struct hb_sanitize_context_t : hb_dispatch_context_t { - hb_sanitize_context_t () : - start (nullptr), end (nullptr), + hb_sanitize_context_t (const char *start_ = nullptr, const char *end_ = nullptr) : + start (start_), end (end_), length (0), max_ops (0), max_subtables (0), recursion_depth (0), - writable (false), edit_count (0), + writable (false), blob (nullptr), num_glyphs (65536), num_glyphs_set (false), @@ -212,14 +215,22 @@ struct hb_sanitize_context_t : void reset_object () { - this->start = this->blob->data; - this->end = this->start + this->blob->length; + if (this->blob) + { + this->start = this->blob->data; + this->end = this->start + this->blob->length; + } this->length = this->end - this->start; assert (this->start <= this->end); /* Must not overflow. */ } - void start_processing () + void start_processing (const char *start_ = nullptr, const char *end_ = nullptr) { + if (start_) + { + this->start = start_; + this->end = end_; + } reset_object (); unsigned m; if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR, &m))) @@ -228,7 +239,6 @@ struct hb_sanitize_context_t : this->max_ops = hb_clamp (m, (unsigned) HB_SANITIZE_MAX_OPS_MIN, (unsigned) HB_SANITIZE_MAX_OPS_MAX); - this->edit_count = 0; this->debug_depth = 0; this->recursion_depth = 0; @@ -241,8 +251,8 @@ struct hb_sanitize_context_t : void end_processing () { DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, - "end [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); + "end [%p..%p]", + this->start, this->end); hb_blob_destroy (this->blob); this->blob = nullptr; @@ -250,9 +260,6 @@ struct hb_sanitize_context_t : this->length = 0; } - unsigned get_edit_count () { return edit_count; } - - bool check_ops(unsigned count) { /* Avoid underflow */ @@ -396,35 +403,6 @@ struct hb_sanitize_context_t : return likely (this->check_point ((const char *) obj + obj->min_size)); } - bool may_edit (const void *base, unsigned int len) - { - if (this->edit_count >= HB_SANITIZE_MAX_EDITS) - return false; - - const char *p = (const char *) base; - this->edit_count++; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "may_edit(%u) [%p..%p] (%u bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "GRANTED" : "DENIED"); - - return this->writable; - } - - template - bool try_set (const Type *obj, const ValueType &v) - { - if (this->may_edit (obj, hb_static_size (Type))) - { - * const_cast (obj) = v; - return true; - } - return false; - } - template hb_blob_t *sanitize_blob (hb_blob_t *blob) { @@ -432,7 +410,6 @@ struct hb_sanitize_context_t : init (blob); - retry: DEBUG_MSG_FUNC (SANITIZE, start, "start"); start_processing (); @@ -446,36 +423,6 @@ struct hb_sanitize_context_t : Type *t = reinterpret_cast (const_cast (start)); sane = t->sanitize (this); - if (sane) - { - if (edit_count) - { - DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %u edits; going for second round", edit_count); - - /* sanitize again to ensure no toe-stepping */ - edit_count = 0; - sane = t->sanitize (this); - if (edit_count) { - DEBUG_MSG_FUNC (SANITIZE, start, "requested %u edits in second round; FAILING", edit_count); - sane = false; - } - } - } - else - { - if (edit_count && !writable) { - start = hb_blob_get_data_writable (blob, nullptr); - end = start + blob->length; - - if (start) - { - writable = true; - /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, start, "retry"); - goto retry; - } - } - } end_processing (); @@ -506,7 +453,6 @@ struct hb_sanitize_context_t : private: int recursion_depth; bool writable; - unsigned int edit_count; hb_blob_t *blob; unsigned int num_glyphs; bool num_glyphs_set; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-script-list.h b/src/java.desktop/share/native/libharfbuzz/hb-script-list.h index f811dbc3408..16347b9d84e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-script-list.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-script-list.h @@ -223,6 +223,10 @@ HB_END_DECLS * @HB_SCRIPT_SUNUWAR: `Sunu`, Since: 10.0.0 * @HB_SCRIPT_TODHRI: `Todr`, Since: 10.0.0 * @HB_SCRIPT_TULU_TIGALARI: `Tutg`, Since: 10.0.0 + * @HB_SCRIPT_BERIA_ERFE: `Berf`, Since: 11.5.0 + * @HB_SCRIPT_SIDETIC: `Sidt`, Since: 11.5.0 + * @HB_SCRIPT_TAI_YO: `Tayo`, Since: 11.5.0 + * @HB_SCRIPT_TOLONG_SIKI: `Tols`, Since: 11.5.0 * @HB_SCRIPT_INVALID: No script set * * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding @@ -461,6 +465,14 @@ typedef enum HB_SCRIPT_TODHRI = HB_TAG ('T','o','d','r'), /*16.0*/ HB_SCRIPT_TULU_TIGALARI = HB_TAG ('T','u','t','g'), /*16.0*/ + /* + * Since 11.5.0 + */ + HB_SCRIPT_BERIA_ERFE = HB_TAG ('B','e','r','f'), /*17.0*/ + HB_SCRIPT_SIDETIC = HB_TAG ('S','i','d','t'), /*17.0*/ + HB_SCRIPT_TAI_YO = HB_TAG ('T','a','y','o'), /*17.0*/ + HB_SCRIPT_TOLONG_SIKI = HB_TAG ('T','o','l','s'), /*17.0*/ + /* No script set. */ HB_SCRIPT_INVALID = HB_TAG_NONE, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh index 704d0ffd12b..9fe99c85ea7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh @@ -34,7 +34,7 @@ #include "hb.hh" #include "hb-blob.hh" #include "hb-map.hh" -#include "hb-pool.hh" +#include "hb-free-pool.hh" #include "hb-subset-serialize.h" @@ -724,7 +724,7 @@ struct hb_serialize_context_t hb_requires (hb_is_iterator (Iterator)), typename ...Ts> void copy_all (Iterator it, Ts&&... ds) - { for (decltype (*it) _ : it) copy (_, std::forward (ds)...); } + { for (decltype (*it) _ : it) copy (_, ds...); } template hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; } @@ -794,7 +794,8 @@ struct hb_serialize_context_t template void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset) { - auto &off = * ((BEInt *) (parent->head + link.position)); + // XXX We should stop assuming big-endian! + auto &off = * ((HBInt *) (parent->head + link.position)); assert (0 == off); check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW); } @@ -814,7 +815,7 @@ struct hb_serialize_context_t } /* Object memory pool. */ - hb_pool_t object_pool; + hb_free_pool_t object_pool; /* Stack of currently under construction objects. */ object_t *current; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh b/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh index f0416d5fa1d..f8cb0998260 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-set-digest.hh @@ -55,11 +55,11 @@ * - For each glyph, if it doesn't match the subtable digest, * skip it. * - * The main filter we use is a combination of four bits-pattern + * The filter we use is a combination of three bits-pattern * filters. A bits-pattern filter checks a number of bits (5 or 6) - * of the input number (glyph-id in this case) and checks whether + * of the input number (glyph-id in most cases) and checks whether * its pattern is amongst the patterns of any of the accepted values. - * The accepted patterns are represented as a "long" integer. The + * The accepted patterns are represented as a "long" integer. Each * check is done using four bitwise operations only. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc index d14f577be00..a36fa14fb28 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc @@ -149,14 +149,11 @@ hb_shape_full (hb_font_t *font, hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); - if (buffer->max_ops <= 0) - buffer->shaping_failed = true; - hb_shape_plan_destroy (shape_plan); if (text_buffer) { - if (res && buffer->successful && !buffer->shaping_failed + if (res && buffer->successful && text_buffer->successful && !buffer->verify (text_buffer, font, @@ -199,6 +196,7 @@ hb_shape (hb_font_t *font, #ifdef HB_EXPERIMENTAL_API +#ifndef HB_NO_VAR static float buffer_advance (hb_buffer_t *buffer) @@ -440,7 +438,7 @@ hb_shape_justify (hb_font_t *font, return true; } - +#endif #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh index f079caf4d35..cb386f26827 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh @@ -57,6 +57,14 @@ HB_SHAPER_IMPLEMENT (directwrite) HB_SHAPER_IMPLEMENT (coretext) #endif +#ifdef HAVE_HARFRUST +HB_SHAPER_IMPLEMENT (harfrust) +#endif + +#ifdef HAVE_KBTS +HB_SHAPER_IMPLEMENT (kbts) +#endif + #ifndef HB_NO_FALLBACK_SHAPE HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */ #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-static.cc b/src/java.desktop/share/native/libharfbuzz/hb-static.cc index 4e70e413651..06cc80b5f38 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-static.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-static.cc @@ -113,27 +113,4 @@ hb_face_t::load_upem () const return ret; } - -#ifndef HB_NO_VAR -bool -_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, - int *lsb) -{ - return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb); -} - -unsigned -_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) -{ - return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical); -} -#endif - -bool -_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb) -{ - return face->table.glyf->get_leading_bearing_without_var_unscaled (gid, is_vertical, lsb); -} - - #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh index 9d00cae6c32..51413b6da44 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh @@ -45,25 +45,25 @@ static const union HB_STRING_ARRAY_TYPE_NAME { * but C++ does not allow that. * https://stackoverflow.com/q/28433862 */ -#define _S(s) char HB_PASTE (str, __LINE__)[sizeof (s)]; +#define HB_STR(s) char HB_PASTE (str, __LINE__)[sizeof (s)]; #include HB_STRING_ARRAY_LIST -#undef _S +#undef HB_STR } st; char str[HB_VAR_ARRAY]; } HB_STRING_ARRAY_POOL_NAME = { { -#define _S(s) s, +#define HB_STR(s) s, #include HB_STRING_ARRAY_LIST -#undef _S +#undef HB_STR } }; static const unsigned int HB_STRING_ARRAY_OFFS_NAME[] = { -#define _S(s) offsetof (union HB_STRING_ARRAY_TYPE_NAME, st.HB_PASTE(str, __LINE__)), +#define HB_STR(s) offsetof (union HB_STRING_ARRAY_TYPE_NAME, st.HB_PASTE(str, __LINE__)), #include HB_STRING_ARRAY_LIST -#undef _S +#undef HB_STR sizeof (HB_STRING_ARRAY_TYPE_NAME) }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh index e7bddaef723..843ad69f2f9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh @@ -861,8 +861,7 @@ struct subr_subsetter_t { // Hack to point vector to static string. auto &b = buffArray.arrayZ[last]; - b.length = 1; - b.arrayZ = const_cast(endchar_str); + b.set_storage (const_cast(endchar_str), 1); } last++; // Skip over gid @@ -877,8 +876,7 @@ struct subr_subsetter_t { // Hack to point vector to static string. auto &b = buffArray.arrayZ[last]; - b.length = 1; - b.arrayZ = const_cast(endchar_str); + b.set_storage (const_cast(endchar_str), 1); } return true; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh index 01987bd258d..8ee02c07dbe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-iup.hh @@ -26,12 +26,27 @@ #define HB_SUBSET_INSTANCER_IUP_HH #include "hb-subset-plan.hh" + +struct iup_scratch_t +{ + hb_vector_t end_points; + hb_vector_t interp_x_deltas; + hb_vector_t interp_y_deltas; + hb_vector_t costs; + hb_vector_t chain; + hb_vector_t rot_indices; + hb_vector_t rot_x_deltas; + hb_vector_t rot_y_deltas; + contour_point_vector_t rot_points; +}; + /* given contour points and deltas, optimize a set of referenced points within error * tolerance. Returns optimized referenced point indices */ HB_INTERNAL bool iup_delta_optimize (const contour_point_vector_t& contour_points, const hb_vector_t& x_deltas, const hb_vector_t& y_deltas, hb_vector_t& opt_indices, /* OUT */ + iup_scratch_t &scratch, double tolerance = 0.0); #endif /* HB_SUBSET_INSTANCER_IUP_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc index c67fee421c2..15227bdab48 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.cc @@ -62,9 +62,10 @@ static inline double supportScalar (double coord, const Triple &tent) return (end - coord) / (end - peak); } -static inline rebase_tent_result_t -_solve (Triple tent, Triple axisLimit, bool negative = false) +static inline void +_solve (Triple tent, Triple axisLimit, rebase_tent_result_t &out, bool negative = false) { + out.reset(); double axisMin = axisLimit.minimum; double axisDef = axisLimit.middle; double axisMax = axisLimit.maximum; @@ -75,14 +76,12 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) // Mirror the problem such that axisDef <= peak if (axisDef > peak) { - rebase_tent_result_t vec = _solve (_reverse_negate (tent), - _reverse_negate (axisLimit), - !negative); + _solve (_reverse_negate (tent), _reverse_negate (axisLimit), out, !negative); - for (auto &p : vec) + for (auto &p : out) p = hb_pair (p.first, _reverse_negate (p.second)); - return vec; + return; } // axisDef <= peak @@ -98,7 +97,7 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) * axisMin axisDef axisMax lower upper */ if (axisMax <= lower && axisMax < peak) - return rebase_tent_result_t{}; // No overlap + return; // No overlap /* case 2: Only the peak and outermost bound fall outside the new limit; * we keep the deltaset, update peak and outermost bound and scale deltas @@ -133,18 +132,18 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) double mult = supportScalar (axisMax, tent); tent = Triple{lower, axisMax, axisMax}; - rebase_tent_result_t vec = _solve (tent, axisLimit); + _solve (tent, axisLimit, out); - for (auto &p : vec) + for (auto &p : out) p = hb_pair (p.first * mult, p.second); - return vec; + return; } // lower <= axisDef <= peak <= axisMax double gain = supportScalar (axisDef, tent); - rebase_tent_result_t out {hb_pair (gain, Triple{})}; + out.push(hb_pair (gain, Triple{})); // First, the positive side @@ -362,8 +361,6 @@ _solve (Triple tent, Triple axisLimit, bool negative = false) out.push (hb_pair (scalar1 - gain, loc1)); out.push (hb_pair (scalar2 - gain, loc2)); } - - return out; } static inline TripleDistances _reverse_triple_distances (const TripleDistances &v) @@ -405,18 +402,21 @@ double renormalizeValue (double v, const Triple &triple, return (-v_distance) /total_distance; } -rebase_tent_result_t -rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances) +void +rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances, + rebase_tent_result_t &out, + rebase_tent_result_t &scratch) { assert (-1.0 <= axisLimit.minimum && axisLimit.minimum <= axisLimit.middle && axisLimit.middle <= axisLimit.maximum && axisLimit.maximum <= +1.0); assert (-2.0 <= tent.minimum && tent.minimum <= tent.middle && tent.middle <= tent.maximum && tent.maximum <= +2.0); assert (tent.middle != 0.0); - rebase_tent_result_t sols = _solve (tent, axisLimit); + rebase_tent_result_t &sols = scratch; + _solve (tent, axisLimit, sols); auto n = [&axisLimit, &axis_triple_distances] (double v) { return renormalizeValue (v, axisLimit, axis_triple_distances); }; - rebase_tent_result_t out; + out.reset(); for (auto &p : sols) { if (!p.first) continue; @@ -429,6 +429,4 @@ rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distance out.push (hb_pair (p.first, Triple{n (t.minimum), n (t.middle), n (t.maximum)})); } - - return out; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh index 9764dcc1725..574d58d53d8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-instancer-solver.hh @@ -42,10 +42,9 @@ struct TripleDistances double positive; }; -struct Triple { - - Triple () : - minimum (0.0), middle (0.0), maximum (0.0) {} +struct Triple +{ + Triple () = default; Triple (double minimum_, double middle_, double maximum_) : minimum (minimum_), middle (middle_), maximum (maximum_) {} @@ -81,10 +80,9 @@ struct Triple { return current; } - - double minimum; - double middle; - double maximum; + double minimum = 0; + double middle = 0; + double maximum = 0; }; using rebase_tent_result_item_t = hb_pair_t; @@ -107,8 +105,10 @@ HB_INTERNAL double renormalizeValue (double v, const Triple &triple, * If tent value is Triple{}, that is a special deltaset that should * be always-enabled (called "gain"). */ -HB_INTERNAL rebase_tent_result_t rebase_tent (Triple tent, - Triple axisLimit, - TripleDistances axis_triple_distances); +HB_INTERNAL void rebase_tent (Triple tent, + Triple axisLimit, + TripleDistances axis_triple_distances, + rebase_tent_result_t &out, + rebase_tent_result_t &scratch); #endif /* HB_SUBSET_INSTANCER_SOLVER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh index ade8278c40f..398fe81eca9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan-member-list.hh @@ -81,6 +81,10 @@ HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gpo HB_SUBSET_PLAN_MEMBER (hb_map_t, gsub_features) HB_SUBSET_PLAN_MEMBER (hb_map_t, gpos_features) +//active features(with duplicates) old index -> new index mapping +HB_SUBSET_PLAN_MEMBER (hb_map_t, gsub_features_w_duplicates) +HB_SUBSET_PLAN_MEMBER (hb_map_t, gpos_features_w_duplicates) + //active feature variation records/condition index with variations HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gsub_feature_record_cond_idx_map) HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(>), gpos_feature_record_cond_idx_map) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc index 3ba726d925e..8c3fd97899b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc @@ -418,7 +418,8 @@ _nameid_closure (hb_subset_plan_t* plan, hb_set_t* drop_tables) { #ifndef HB_NO_STYLE - plan->source->table.STAT->collect_name_ids (&plan->user_axes_location, &plan->name_ids); + if (!drop_tables->has (HB_OT_TAG_STAT)) + plan->source->table.STAT->collect_name_ids (&plan->user_axes_location, &plan->name_ids); #endif #ifndef HB_NO_VAR if (!plan->all_axes_pinned) @@ -676,7 +677,8 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, return; #ifndef HB_NO_VAR - normalize_axes_location (face, this); + if (!check_success (normalize_axes_location (face, this))) + return; #endif _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this); @@ -697,6 +699,15 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, return; } +#ifdef HB_EXPERIMENTAL_API + if ((input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS) && + (input->flags & HB_SUBSET_FLAGS_RETAIN_NUM_GLYPHS)) { + // We've been requested to maintain the num glyphs count from the + // input face. + _num_output_glyphs = source->get_num_glyphs (); + } +#endif + _create_glyph_map_gsub ( &_glyphset_gsub, glyph_map, @@ -710,10 +721,10 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second); } - bounds_width_vec.resize (_num_output_glyphs, false); + bounds_width_vec.resize_dirty (_num_output_glyphs); for (auto &v : bounds_width_vec) v = 0xFFFFFFFF; - bounds_height_vec.resize (_num_output_glyphs, false); + bounds_height_vec.resize_dirty (_num_output_glyphs); for (auto &v : bounds_height_vec) v = 0xFFFFFFFF; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh index 8a6574365ed..0362ed7292e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh @@ -300,6 +300,7 @@ struct hb_subset_plan_t // compile times more reasonable: // - hb-subset-plan.cc // - hb-subset-plan-layout.cc +// - hb-subset-plan-var.cc // // The functions below are those needed to connect the split files // above together. @@ -332,7 +333,7 @@ generate_varstore_inner_maps (const hb_set_t& varidx_set, unsigned subtable_count, hb_vector_t &inner_maps /* OUT */); -HB_INTERNAL void +HB_INTERNAL bool normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan); HB_INTERNAL void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc index 079c94eddfe..51134ed09ce 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc @@ -25,65 +25,19 @@ */ #include "hb.hh" + #include "hb-open-type.hh" +#include "hb-open-file.hh" #include "hb-subset.hh" - -#include "hb-open-file.hh" -#include "hb-ot-cmap-table.hh" -#include "hb-ot-glyf-table.hh" -#include "hb-ot-hdmx-table.hh" -#include "hb-ot-head-table.hh" -#include "hb-ot-hhea-table.hh" -#include "hb-ot-hmtx-table.hh" -#include "hb-ot-maxp-table.hh" -#include "OT/Color/CBDT/CBDT.hh" -#include "OT/Color/COLR/COLR.hh" -#include "OT/Color/CPAL/CPAL.hh" -#include "OT/Color/sbix/sbix.hh" -#include "hb-ot-os2-table.hh" -#include "hb-ot-post-table.hh" -#include "hb-ot-post-table-v2subset.hh" -#include "hb-ot-cff1-table.hh" -#include "hb-ot-cff2-table.hh" -#include "hb-ot-vorg-table.hh" -#include "hb-ot-name-table.hh" -#include "hb-ot-layout-base-table.hh" -#include "hb-ot-layout-gsub-table.hh" -#include "hb-ot-layout-gpos-table.hh" -#include "hb-ot-var-avar-table.hh" -#include "hb-ot-var-cvar-table.hh" -#include "hb-ot-var-fvar-table.hh" -#include "hb-ot-var-gvar-table.hh" -#include "hb-ot-var-hvar-table.hh" -#include "hb-ot-var-mvar-table.hh" -#include "hb-ot-math-table.hh" -#include "hb-ot-stat-table.hh" -#include "hb-repacker.hh" +#include "hb-subset-table.hh" #include "hb-subset-accelerator.hh" -using OT::Layout::GSUB; -using OT::Layout::GPOS; - - -#ifndef HB_NO_SUBSET_CFF -template<> -struct hb_subset_plan_t::source_table_loader -{ - auto operator () (hb_subset_plan_t *plan) - HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff1_accel : - plan->inprogress_accelerator ? plan->inprogress_accelerator->cff1_accel : - plan->cff1_accel) -}; -template<> -struct hb_subset_plan_t::source_table_loader -{ - auto operator () (hb_subset_plan_t *plan) - HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff2_accel : - plan->inprogress_accelerator ? plan->inprogress_accelerator->cff2_accel : - plan->cff2_accel) -}; -#endif +#include "hb-ot-cmap-table.hh" +#include "hb-ot-var-cvar-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-ot-post-table-v2subset.hh" /** @@ -116,56 +70,56 @@ hb_user_data_key_t _hb_subset_accelerator_user_data_key = {}; * if we are unable to list the tables in a face. */ static hb_tag_t known_tables[] { - HB_TAG ('a', 'v', 'a', 'r'), - HB_OT_TAG_BASE, - HB_OT_TAG_CBDT, - HB_OT_TAG_CBLC, - HB_OT_TAG_CFF1, - HB_OT_TAG_CFF2, - HB_OT_TAG_cmap, - HB_OT_TAG_COLR, - HB_OT_TAG_CPAL, - HB_TAG ('c', 'v', 'a', 'r'), - HB_TAG ('c', 'v', 't', ' '), - HB_TAG ('D', 'S', 'I', 'G'), - HB_TAG ('E', 'B', 'D', 'T'), - HB_TAG ('E', 'B', 'L', 'C'), - HB_TAG ('E', 'B', 'S', 'C'), - HB_TAG ('f', 'p', 'g', 'm'), - HB_TAG ('f', 'v', 'a', 'r'), - HB_TAG ('g', 'a', 's', 'p'), - HB_OT_TAG_GDEF, - HB_OT_TAG_glyf, - HB_OT_TAG_GPOS, - HB_OT_TAG_GSUB, - HB_OT_TAG_gvar, - HB_OT_TAG_hdmx, - HB_OT_TAG_head, - HB_OT_TAG_hhea, - HB_OT_TAG_hmtx, - HB_OT_TAG_HVAR, - HB_OT_TAG_JSTF, - HB_TAG ('k', 'e', 'r', 'n'), - HB_OT_TAG_loca, - HB_TAG ('L', 'T', 'S', 'H'), - HB_OT_TAG_MATH, - HB_OT_TAG_maxp, - HB_TAG ('M', 'E', 'R', 'G'), - HB_TAG ('m', 'e', 't', 'a'), - HB_TAG ('M', 'V', 'A', 'R'), - HB_TAG ('P', 'C', 'L', 'T'), - HB_OT_TAG_post, - HB_TAG ('p', 'r', 'e', 'p'), - HB_OT_TAG_sbix, - HB_TAG ('S', 'T', 'A', 'T'), - HB_TAG ('S', 'V', 'G', ' '), - HB_TAG ('V', 'D', 'M', 'X'), - HB_OT_TAG_vhea, - HB_OT_TAG_vmtx, - HB_OT_TAG_VORG, - HB_OT_TAG_VVAR, - HB_OT_TAG_name, - HB_OT_TAG_OS2 + HB_TAG('a','v','a','r'), + HB_TAG('B','A','S','E'), + HB_TAG('C','B','D','T'), + HB_TAG('C','B','L','C'), + HB_TAG('C','F','F',' '), + HB_TAG('C','F','F','2'), + HB_TAG('c','m','a','p'), + HB_TAG('C','O','L','R'), + HB_TAG('C','P','A','L'), + HB_TAG('c','v','a','r'), + HB_TAG('c','v','t',' '), + HB_TAG('D','S','I','G'), + HB_TAG('E','B','D','T'), + HB_TAG('E','B','L','C'), + HB_TAG('E','B','S','C'), + HB_TAG('f','p','g','m'), + HB_TAG('f','v','a','r'), + HB_TAG('g','a','s','p'), + HB_TAG('G','D','E','F'), + HB_TAG('g','l','y','f'), + HB_TAG('G','P','O','S'), + HB_TAG('G','S','U','B'), + HB_TAG('g','v','a','r'), + HB_TAG('h','d','m','x'), + HB_TAG('h','e','a','d'), + HB_TAG('h','h','e','a'), + HB_TAG('h','m','t','x'), + HB_TAG('H','V','A','R'), + HB_TAG('J','S','T','F'), + HB_TAG('k','e','r','n'), + HB_TAG('l','o','c','a'), + HB_TAG('L','T','S','H'), + HB_TAG('M','A','T','H'), + HB_TAG('m','a','x','p'), + HB_TAG('M','E','R','G'), + HB_TAG('m','e','t','a'), + HB_TAG('M','V','A','R'), + HB_TAG('P','C','L','T'), + HB_TAG('p','o','s','t'), + HB_TAG('p','r','e','p'), + HB_TAG('s','b','i','x'), + HB_TAG('S','T','A','T'), + HB_TAG('S','V','G',' '), + HB_TAG('V','D','M','X'), + HB_TAG('v','h','e','a'), + HB_TAG('v','m','t','x'), + HB_TAG('V','O','R','G'), + HB_TAG('V','V','A','R'), + HB_TAG('n','a','m','e'), + HB_TAG('O','S','/','2') }; static bool _table_is_empty (const hb_face_t *face, hb_tag_t tag) @@ -213,169 +167,6 @@ _get_table_tags (const hb_subset_plan_t* plan, } -static unsigned -_plan_estimate_subset_table_size (hb_subset_plan_t *plan, - unsigned table_len, - hb_tag_t table_tag) -{ - unsigned src_glyphs = plan->source->get_num_glyphs (); - unsigned dst_glyphs = plan->glyphset ()->get_population (); - - unsigned bulk = 8192; - /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's - * because those are expensive to subset, so giving them more room is fine. */ - bool same_size = table_tag == HB_OT_TAG_GSUB || - table_tag == HB_OT_TAG_GPOS || - table_tag == HB_OT_TAG_name; - - if (plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS) - { - if (table_tag == HB_OT_TAG_CFF1) - { - /* Add some extra room for the CFF charset. */ - bulk += src_glyphs * 16; - } - else if (table_tag == HB_OT_TAG_CFF2) - { - /* Just extra CharString offsets. */ - bulk += src_glyphs * 4; - } - } - - if (unlikely (!src_glyphs) || same_size) - return bulk + table_len; - - return bulk + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs)); -} - -/* - * Repack the serialization buffer if any offset overflows exist. - */ -static hb_blob_t* -_repack (hb_tag_t tag, const hb_serialize_context_t& c) -{ - if (!c.offset_overflow ()) - return c.copy_blob (); - - hb_blob_t* result = hb_resolve_overflows (c.object_graph (), tag); - - if (unlikely (!result)) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c offset overflow resolution failed.", - HB_UNTAG (tag)); - return nullptr; - } - - return result; -} - -template -static -bool -_try_subset (const TableType *table, - hb_vector_t* buf, - hb_subset_context_t* c /* OUT */) -{ - c->serializer->start_serialize (); - if (c->serializer->in_error ()) return false; - - bool needed = table->subset (c); - if (!c->serializer->ran_out_of_room ()) - { - c->serializer->end_serialize (); - return needed; - } - - unsigned buf_size = buf->allocated; - buf_size = buf_size * 2 + 16; - - - - - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", - HB_UNTAG (c->table_tag), buf_size); - - if (unlikely (buf_size > c->source_blob->length * 256 || - !buf->alloc_exact (buf_size))) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", - HB_UNTAG (c->table_tag), buf_size); - return needed; - } - - c->serializer->reset (buf->arrayZ, buf->allocated); - return _try_subset (table, buf, c); -} - -template -static auto _do_destroy (T &t, hb_priority<1>) HB_RETURN (void, t.destroy ()) - -template -static void _do_destroy (T &t, hb_priority<0>) {} - -template -static bool -_subset (hb_subset_plan_t *plan, hb_vector_t &buf) -{ - auto &&source_blob = plan->source_table (); - auto *table = source_blob.get (); - - hb_tag_t tag = TableType::tableTag; - hb_blob_t *blob = source_blob.get_blob(); - if (unlikely (!blob || !blob->data)) - { - DEBUG_MSG (SUBSET, nullptr, - "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); - _do_destroy (source_blob, hb_prioritize); - return false; - } - - unsigned buf_size = _plan_estimate_subset_table_size (plan, blob->length, TableType::tableTag); - DEBUG_MSG (SUBSET, nullptr, - "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size); - if (unlikely (!buf.alloc (buf_size))) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size); - _do_destroy (source_blob, hb_prioritize); - return false; - } - - bool needed = false; - hb_serialize_context_t serializer (buf.arrayZ, buf.allocated); - { - hb_subset_context_t c (blob, plan, &serializer, tag); - needed = _try_subset (table, &buf, &c); - } - _do_destroy (source_blob, hb_prioritize); - - if (serializer.in_error () && !serializer.only_offset_overflow ()) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset FAILED!", HB_UNTAG (tag)); - return false; - } - - if (!needed) - { - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); - return true; - } - - bool result = false; - hb_blob_t *dest_blob = _repack (tag, serializer); - if (dest_blob) - { - DEBUG_MSG (SUBSET, nullptr, - "OT::%c%c%c%c final subset table size: %u bytes.", - HB_UNTAG (tag), dest_blob->length); - result = plan->add_table (tag, dest_blob); - hb_blob_destroy (dest_blob); - } - - DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset %s", - HB_UNTAG (tag), result ? "success" : "FAILED!"); - return result; -} - static bool _is_table_present (hb_face_t *source, hb_tag_t tag) { @@ -407,34 +198,34 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) switch (tag) { - case HB_TAG ('c','v','a','r'): /* hint table, fallthrough */ + case HB_TAG('c','v','a','r'): /* hint table, fallthrough */ return plan->all_axes_pinned || (plan->flags & HB_SUBSET_FLAGS_NO_HINTING); - case HB_TAG ('c','v','t',' '): /* hint table, fallthrough */ - case HB_TAG ('f','p','g','m'): /* hint table, fallthrough */ - case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */ - case HB_TAG ('h','d','m','x'): /* hint table, fallthrough */ - case HB_TAG ('V','D','M','X'): /* hint table, fallthrough */ + case HB_TAG('c','v','t',' '): /* hint table, fallthrough */ + case HB_TAG('f','p','g','m'): /* hint table, fallthrough */ + case HB_TAG('p','r','e','p'): /* hint table, fallthrough */ + case HB_TAG('h','d','m','x'): /* hint table, fallthrough */ + case HB_TAG('V','D','M','X'): /* hint table, fallthrough */ return plan->flags & HB_SUBSET_FLAGS_NO_HINTING; #ifdef HB_NO_SUBSET_LAYOUT // Drop Layout Tables if requested. - case HB_OT_TAG_GDEF: - case HB_OT_TAG_GPOS: - case HB_OT_TAG_GSUB: - case HB_TAG ('m','o','r','x'): - case HB_TAG ('m','o','r','t'): - case HB_TAG ('k','e','r','x'): - case HB_TAG ('k','e','r','n'): + case HB_TAG('G','D','E','F'): + case HB_TAG('G','P','O','S'): + case HB_TAG('G','S','U','B'): + case HB_TAG('m','o','r','x'): + case HB_TAG('m','o','r','t'): + case HB_TAG('k','e','r','x'): + case HB_TAG('k','e','r','n'): return true; #endif - case HB_TAG ('a','v','a','r'): - case HB_TAG ('f','v','a','r'): - case HB_TAG ('g','v','a','r'): - case HB_OT_TAG_HVAR: - case HB_OT_TAG_VVAR: - case HB_TAG ('M','V','A','R'): + case HB_TAG('a','v','a','r'): + case HB_TAG('f','v','a','r'): + case HB_TAG('g','v','a','r'): + case HB_TAG('H','V','A','R'): + case HB_TAG('V','V','A','R'): + case HB_TAG('M','V','A','R'): return plan->all_axes_pinned; default: @@ -442,15 +233,6 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) } } -static bool -_passthrough (hb_subset_plan_t *plan, hb_tag_t tag) -{ - hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); - bool result = plan->add_table (tag, source_table); - hb_blob_destroy (source_table); - return result; -} - static bool _dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, const hb_set_t &subsetted_tags, @@ -458,13 +240,13 @@ _dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, { switch (tag) { - case HB_OT_TAG_hmtx: - case HB_OT_TAG_vmtx: - case HB_OT_TAG_maxp: - case HB_OT_TAG_OS2: - return !plan->normalized_coords || !pending_subset_tags.has (HB_OT_TAG_glyf); - case HB_OT_TAG_GPOS: - return plan->all_axes_pinned || !pending_subset_tags.has (HB_OT_TAG_GDEF); + case HB_TAG('h','m','t','x'): + case HB_TAG('v','m','t','x'): + case HB_TAG('m','a','x','p'): + case HB_TAG('O','S','/','2'): + return !plan->normalized_coords || !pending_subset_tags.has (HB_TAG('g','l','y','f')); + case HB_TAG('G','P','O','S'): + return plan->all_axes_pinned || !pending_subset_tags.has (HB_TAG('G','D','E','F')); default: return true; } @@ -476,88 +258,48 @@ _subset_table (hb_subset_plan_t *plan, hb_tag_t tag) { if (plan->no_subset_tables.has (tag)) { - return _passthrough (plan, tag); + return _hb_subset_table_passthrough (plan, tag); } DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag)); + + bool success; + if (_hb_subset_table_layout (plan, buf, tag, &success) || + _hb_subset_table_var (plan, buf, tag, &success) || + _hb_subset_table_cff (plan, buf, tag, &success) || + _hb_subset_table_color (plan, buf, tag, &success) || + _hb_subset_table_other (plan, buf, tag, &success)) + return success; + + switch (tag) { - case HB_OT_TAG_glyf: return _subset (plan, buf); - case HB_OT_TAG_hdmx: return _subset (plan, buf); - case HB_OT_TAG_name: return _subset (plan, buf); - case HB_OT_TAG_head: - if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf)) + case HB_TAG('h','e','a','d'): + if (_is_table_present (plan->source, HB_TAG('g','l','y','f')) && !_should_drop_table (plan, HB_TAG('g','l','y','f'))) return true; /* skip head, handled by glyf */ - return _subset (plan, buf); - case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */ - case HB_OT_TAG_hmtx: return _subset (plan, buf); - case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */ - case HB_OT_TAG_vmtx: return _subset (plan, buf); - case HB_OT_TAG_maxp: return _subset (plan, buf); - case HB_OT_TAG_sbix: return _subset (plan, buf); - case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */ - case HB_OT_TAG_cmap: return _subset (plan, buf); - case HB_OT_TAG_OS2 : return _subset (plan, buf); - case HB_OT_TAG_post: return _subset (plan, buf); - case HB_OT_TAG_COLR: return _subset (plan, buf); - case HB_OT_TAG_CPAL: return _subset (plan, buf); - case HB_OT_TAG_CBLC: return _subset (plan, buf); - case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ - case HB_OT_TAG_MATH: return _subset (plan, buf); - case HB_OT_TAG_BASE: return _subset (plan, buf); + return _hb_subset_table (plan, buf); -#ifndef HB_NO_SUBSET_CFF - case HB_OT_TAG_CFF1: return _subset (plan, buf); - case HB_OT_TAG_CFF2: return _subset (plan, buf); - case HB_OT_TAG_VORG: return _subset (plan, buf); -#endif - -#ifndef HB_NO_SUBSET_LAYOUT - case HB_OT_TAG_GDEF: return _subset (plan, buf); - case HB_OT_TAG_GSUB: return _subset (plan, buf); - case HB_OT_TAG_GPOS: return _subset (plan, buf); - case HB_OT_TAG_gvar: return _subset (plan, buf); - case HB_OT_TAG_HVAR: return _subset (plan, buf); - case HB_OT_TAG_VVAR: return _subset (plan, buf); -#endif + case HB_TAG('S','T','A','T'): + if (!plan->user_axes_location.is_empty ()) return _hb_subset_table (plan, buf); + else return _hb_subset_table_passthrough (plan, tag); + case HB_TAG('c','v','t',' '): #ifndef HB_NO_VAR - case HB_OT_TAG_fvar: - if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); - return _subset (plan, buf); - case HB_OT_TAG_avar: - if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); - return _subset (plan, buf); - case HB_OT_TAG_cvar: - if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); - return _subset (plan, buf); - case HB_OT_TAG_MVAR: - if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); - return _subset (plan, buf); -#endif - - case HB_OT_TAG_STAT: - if (!plan->user_axes_location.is_empty ()) return _subset (plan, buf); - else return _passthrough (plan, tag); - - case HB_TAG ('c', 'v', 't', ' '): -#ifndef HB_NO_VAR - if (_is_table_present (plan->source, HB_OT_TAG_cvar) && + if (_is_table_present (plan->source, HB_TAG('c','v','a','r')) && plan->normalized_coords && !plan->pinned_at_default) { auto &cvar = *plan->source->table.cvar; return OT::cvar::add_cvt_and_apply_deltas (plan, cvar.get_tuple_var_data (), &cvar); } #endif - return _passthrough (plan, tag); - - default: - if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) - return _passthrough (plan, tag); - - // Drop table - return true; + return _hb_subset_table_passthrough (plan, tag); } + + if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) + return _hb_subset_table_passthrough (plan, tag); + + // Drop table + return true; } static void _attach_accelerator_data (hb_subset_plan_t* plan, @@ -707,108 +449,4 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) end: return success ? hb_face_reference (plan->dest) : nullptr; -} - - -#ifdef HB_EXPERIMENTAL_API - -#include "hb-ot-cff1-table.hh" - -template -static hb_blob_t* get_charstrings_data(accel_t& accel, hb_codepoint_t glyph_index) { - if (!accel.is_valid()) { - return hb_blob_get_empty (); - } - - hb_ubytes_t bytes = (*accel.charStrings)[glyph_index]; - if (!bytes) { - return hb_blob_get_empty (); - } - - hb_blob_t* cff_blob = accel.get_blob(); - uint32_t length; - const char* cff_data = hb_blob_get_data(cff_blob, &length) ; - - long int offset = (const char*) bytes.arrayZ - cff_data; - if (offset < 0 || offset > INT32_MAX) { - return hb_blob_get_empty (); - } - - return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, bytes.length); -} - -template -static hb_blob_t* get_charstrings_index(accel_t& accel) { - if (!accel.is_valid()) { - return hb_blob_get_empty (); - } - - const char* charstrings_start = (const char*) accel.charStrings; - unsigned charstrings_length = accel.charStrings->get_size(); - - hb_blob_t* cff_blob = accel.get_blob(); - uint32_t length; - const char* cff_data = hb_blob_get_data(cff_blob, &length) ; - - long int offset = charstrings_start - cff_data; - if (offset < 0 || offset > INT32_MAX) { - return hb_blob_get_empty (); - } - - return hb_blob_create_sub_blob(cff_blob, (uint32_t) offset, charstrings_length); -} - -/** - * hb_subset_cff_get_charstring_data: - * @face: A face object - * @glyph_index: Glyph index to get data for. - * - * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. - * - * XSince: EXPERIMENTAL - **/ -HB_EXTERN hb_blob_t* -hb_subset_cff_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { - return get_charstrings_data(*face->table.cff1, glyph_index); -} - -/** - * hb_subset_cff_get_charstrings_index: - * @face: A face object - * - * Returns the raw CFF CharStrings INDEX from the CFF table. - * - * XSince: EXPERIMENTAL - **/ -HB_EXTERN hb_blob_t* -hb_subset_cff_get_charstrings_index (hb_face_t* face) { - return get_charstrings_index (*face->table.cff1); -} - -/** - * hb_subset_cff2_get_charstring_data: - * @face: A face object - * @glyph_index: Glyph index to get data for. - * - * Returns the raw outline data from the CFF/CFF2 table associated with the given glyph index. - * - * XSince: EXPERIMENTAL - **/ -HB_EXTERN hb_blob_t* -hb_subset_cff2_get_charstring_data(hb_face_t* face, hb_codepoint_t glyph_index) { - return get_charstrings_data(*face->table.cff2, glyph_index); -} - -/** - * hb_subset_cff2_get_charstrings_index: - * @face: A face object - * - * Returns the raw CFF2 CharStrings INDEX from the CFF2 table. - * - * XSince: EXPERIMENTAL - **/ -HB_EXTERN hb_blob_t* -hb_subset_cff2_get_charstrings_index (hb_face_t* face) { - return get_charstrings_index (*face->table.cff2); -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.h b/src/java.desktop/share/native/libharfbuzz/hb-subset.h index 3eccd25738e..fd2908e8308 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.h @@ -80,6 +80,10 @@ typedef struct hb_subset_plan_t hb_subset_plan_t; * @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset * to allow it to be used with incremental font transfer IFTB patches. Primarily, * this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL + * @HB_SUBSET_FLAGS_RETAIN_NUM_GLYPHS: If this flag is set along side + * HB_SUBSET_FLAGS_RETAIN_GIDS then the number of glyphs in the font won't + * be reduced as a result of subsetting. If necessary empty glyphs will be + * included at the end of the font to keep the number of glyphs unchanged. * * List of boolean properties that can be configured on the subset input. * @@ -101,6 +105,7 @@ typedef enum { /*< flags >*/ HB_SUBSET_FLAGS_NO_BIDI_CLOSURE = 0x00000800u, #ifdef HB_EXPERIMENTAL_API HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00001000u, + HB_SUBSET_FLAGS_RETAIN_NUM_GLYPHS = 0x00002000u, #endif } hb_subset_flags_t; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset.hh index bc7c24c94de..009919764f5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.hh @@ -70,5 +70,4 @@ struct hb_subset_context_t : table_tag (table_tag_) {} }; - #endif /* HB_SUBSET_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh index 8731a0bcf8d..868428ab1e7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh @@ -2,9 +2,9 @@ /* * The following table is generated by running: * - * ./gen-ucd-table.py ucd.nounihan.grouped.xml + * ./gen-ucd-table.py ucd.nounihan.grouped.xml hb-script-list.h * - * on file with this description: Unicode 16.0.0 + * on file with this description: Unicode 17.0.0 */ #ifndef HB_UCD_TABLE_HH @@ -12,8 +12,9 @@ #include "hb.hh" -static const hb_script_t -_hb_ucd_sc_map[172] = +#include + +static const hb_script_t _hb_ucd_sc_map[176]= { HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, HB_SCRIPT_UNKNOWN, HB_SCRIPT_ARABIC, @@ -101,1040 +102,805 @@ _hb_ucd_sc_map[172] = HB_SCRIPT_GURUNG_KHEMA, HB_SCRIPT_KIRAT_RAI, HB_SCRIPT_OL_ONAL, HB_SCRIPT_SUNUWAR, HB_SCRIPT_TODHRI, HB_SCRIPT_TULU_TIGALARI, + HB_SCRIPT_BERIA_ERFE, HB_SCRIPT_SIDETIC, + HB_SCRIPT_TAI_YO, HB_SCRIPT_TOLONG_SIKI, }; -static const uint16_t -_hb_ucd_dm1_p0_map[825] = +static const uint16_t _hb_ucd_dm1_p0_map[825]= { - 0x003Bu, 0x004Bu, 0x0060u, 0x00B4u, 0x00B7u, 0x00C5u, 0x02B9u, 0x0300u, - 0x0301u, 0x0313u, 0x0385u, 0x0386u, 0x0388u, 0x0389u, 0x038Au, 0x038Cu, - 0x038Eu, 0x038Fu, 0x0390u, 0x03A9u, 0x03ACu, 0x03ADu, 0x03AEu, 0x03AFu, - 0x03B0u, 0x03B9u, 0x03CCu, 0x03CDu, 0x03CEu, 0x2002u, 0x2003u, 0x3008u, - 0x3009u, 0x349Eu, 0x34B9u, 0x34BBu, 0x34DFu, 0x3515u, 0x36EEu, 0x36FCu, - 0x3781u, 0x382Fu, 0x3862u, 0x387Cu, 0x38C7u, 0x38E3u, 0x391Cu, 0x393Au, - 0x3A2Eu, 0x3A6Cu, 0x3AE4u, 0x3B08u, 0x3B19u, 0x3B49u, 0x3B9Du, 0x3C18u, - 0x3C4Eu, 0x3D33u, 0x3D96u, 0x3EACu, 0x3EB8u, 0x3F1Bu, 0x3FFCu, 0x4008u, - 0x4018u, 0x4039u, 0x4046u, 0x4096u, 0x40E3u, 0x412Fu, 0x4202u, 0x4227u, - 0x42A0u, 0x4301u, 0x4334u, 0x4359u, 0x43D5u, 0x43D9u, 0x440Bu, 0x446Bu, - 0x452Bu, 0x455Du, 0x4561u, 0x456Bu, 0x45D7u, 0x45F9u, 0x4635u, 0x46BEu, - 0x46C7u, 0x4995u, 0x49E6u, 0x4A6Eu, 0x4A76u, 0x4AB2u, 0x4B33u, 0x4BCEu, - 0x4CCEu, 0x4CEDu, 0x4CF8u, 0x4D56u, 0x4E0Du, 0x4E26u, 0x4E32u, 0x4E38u, - 0x4E39u, 0x4E3Du, 0x4E41u, 0x4E82u, 0x4E86u, 0x4EAEu, 0x4EC0u, 0x4ECCu, - 0x4EE4u, 0x4F60u, 0x4F80u, 0x4F86u, 0x4F8Bu, 0x4FAEu, 0x4FBBu, 0x4FBFu, - 0x5002u, 0x502Bu, 0x507Au, 0x5099u, 0x50CFu, 0x50DAu, 0x50E7u, 0x5140u, - 0x5145u, 0x514Du, 0x5154u, 0x5164u, 0x5167u, 0x5168u, 0x5169u, 0x516Du, - 0x5177u, 0x5180u, 0x518Du, 0x5192u, 0x5195u, 0x5197u, 0x51A4u, 0x51ACu, - 0x51B5u, 0x51B7u, 0x51C9u, 0x51CCu, 0x51DCu, 0x51DEu, 0x51F5u, 0x5203u, - 0x5207u, 0x5217u, 0x5229u, 0x523Au, 0x523Bu, 0x5246u, 0x5272u, 0x5277u, - 0x5289u, 0x529Bu, 0x52A3u, 0x52B3u, 0x52C7u, 0x52C9u, 0x52D2u, 0x52DEu, - 0x52E4u, 0x52F5u, 0x52FAu, 0x5305u, 0x5306u, 0x5317u, 0x533Fu, 0x5349u, - 0x5351u, 0x535Au, 0x5373u, 0x5375u, 0x537Du, 0x537Fu, 0x53C3u, 0x53CAu, - 0x53DFu, 0x53E5u, 0x53EBu, 0x53F1u, 0x5406u, 0x540Fu, 0x541Du, 0x5438u, - 0x5442u, 0x5448u, 0x5468u, 0x549Eu, 0x54A2u, 0x54BDu, 0x54F6u, 0x5510u, - 0x5553u, 0x5555u, 0x5563u, 0x5584u, 0x5587u, 0x5599u, 0x559Du, 0x55ABu, - 0x55B3u, 0x55C0u, 0x55C2u, 0x55E2u, 0x5606u, 0x5651u, 0x5668u, 0x5674u, - 0x56F9u, 0x5716u, 0x5717u, 0x578Bu, 0x57CEu, 0x57F4u, 0x580Du, 0x5831u, - 0x5832u, 0x5840u, 0x585Au, 0x585Eu, 0x58A8u, 0x58ACu, 0x58B3u, 0x58D8u, - 0x58DFu, 0x58EEu, 0x58F2u, 0x58F7u, 0x5906u, 0x591Au, 0x5922u, 0x5944u, - 0x5948u, 0x5951u, 0x5954u, 0x5962u, 0x5973u, 0x59D8u, 0x59ECu, 0x5A1Bu, - 0x5A27u, 0x5A62u, 0x5A66u, 0x5AB5u, 0x5B08u, 0x5B28u, 0x5B3Eu, 0x5B85u, - 0x5BC3u, 0x5BD8u, 0x5BE7u, 0x5BEEu, 0x5BF3u, 0x5BFFu, 0x5C06u, 0x5C22u, - 0x5C3Fu, 0x5C60u, 0x5C62u, 0x5C64u, 0x5C65u, 0x5C6Eu, 0x5C8Du, 0x5CC0u, - 0x5D19u, 0x5D43u, 0x5D50u, 0x5D6Bu, 0x5D6Eu, 0x5D7Cu, 0x5DB2u, 0x5DBAu, - 0x5DE1u, 0x5DE2u, 0x5DFDu, 0x5E28u, 0x5E3Du, 0x5E69u, 0x5E74u, 0x5EA6u, - 0x5EB0u, 0x5EB3u, 0x5EB6u, 0x5EC9u, 0x5ECAu, 0x5ED2u, 0x5ED3u, 0x5ED9u, - 0x5EECu, 0x5EFEu, 0x5F04u, 0x5F22u, 0x5F53u, 0x5F62u, 0x5F69u, 0x5F6Bu, - 0x5F8Bu, 0x5F9Au, 0x5FA9u, 0x5FADu, 0x5FCDu, 0x5FD7u, 0x5FF5u, 0x5FF9u, - 0x6012u, 0x601Cu, 0x6075u, 0x6081u, 0x6094u, 0x60C7u, 0x60D8u, 0x60E1u, - 0x6108u, 0x6144u, 0x6148u, 0x614Cu, 0x614Eu, 0x6160u, 0x6168u, 0x617Au, - 0x618Eu, 0x6190u, 0x61A4u, 0x61AFu, 0x61B2u, 0x61DEu, 0x61F2u, 0x61F6u, - 0x6200u, 0x6210u, 0x621Bu, 0x622Eu, 0x6234u, 0x625Du, 0x62B1u, 0x62C9u, - 0x62CFu, 0x62D3u, 0x62D4u, 0x62FCu, 0x62FEu, 0x633Du, 0x6350u, 0x6368u, - 0x637Bu, 0x6383u, 0x63A0u, 0x63A9u, 0x63C4u, 0x63C5u, 0x63E4u, 0x641Cu, - 0x6422u, 0x6452u, 0x6469u, 0x6477u, 0x647Eu, 0x649Au, 0x649Du, 0x64C4u, - 0x654Fu, 0x6556u, 0x656Cu, 0x6578u, 0x6599u, 0x65C5u, 0x65E2u, 0x65E3u, - 0x6613u, 0x6649u, 0x6674u, 0x6688u, 0x6691u, 0x669Cu, 0x66B4u, 0x66C6u, - 0x66F4u, 0x66F8u, 0x6700u, 0x6717u, 0x671Bu, 0x6721u, 0x674Eu, 0x6753u, - 0x6756u, 0x675Eu, 0x677Bu, 0x6785u, 0x6797u, 0x67F3u, 0x67FAu, 0x6817u, - 0x681Fu, 0x6852u, 0x6881u, 0x6885u, 0x688Eu, 0x68A8u, 0x6914u, 0x6942u, - 0x69A3u, 0x69EAu, 0x6A02u, 0x6A13u, 0x6AA8u, 0x6AD3u, 0x6ADBu, 0x6B04u, - 0x6B21u, 0x6B54u, 0x6B72u, 0x6B77u, 0x6B79u, 0x6B9Fu, 0x6BAEu, 0x6BBAu, - 0x6BBBu, 0x6C4Eu, 0x6C67u, 0x6C88u, 0x6CBFu, 0x6CCCu, 0x6CCDu, 0x6CE5u, - 0x6D16u, 0x6D1Bu, 0x6D1Eu, 0x6D34u, 0x6D3Eu, 0x6D41u, 0x6D69u, 0x6D6Au, - 0x6D77u, 0x6D78u, 0x6D85u, 0x6DCBu, 0x6DDAu, 0x6DEAu, 0x6DF9u, 0x6E1Au, - 0x6E2Fu, 0x6E6Eu, 0x6E9Cu, 0x6EBAu, 0x6EC7u, 0x6ECBu, 0x6ED1u, 0x6EDBu, - 0x6F0Fu, 0x6F22u, 0x6F23u, 0x6F6Eu, 0x6FC6u, 0x6FEBu, 0x6FFEu, 0x701Bu, - 0x701Eu, 0x7039u, 0x704Au, 0x7070u, 0x7077u, 0x707Du, 0x7099u, 0x70ADu, - 0x70C8u, 0x70D9u, 0x7145u, 0x7149u, 0x716Eu, 0x719Cu, 0x71CEu, 0x71D0u, - 0x7210u, 0x721Bu, 0x7228u, 0x722Bu, 0x7235u, 0x7250u, 0x7262u, 0x7280u, - 0x7295u, 0x72AFu, 0x72C0u, 0x72FCu, 0x732Au, 0x7375u, 0x737Au, 0x7387u, - 0x738Bu, 0x73A5u, 0x73B2u, 0x73DEu, 0x7406u, 0x7409u, 0x7422u, 0x7447u, - 0x745Cu, 0x7469u, 0x7471u, 0x7485u, 0x7489u, 0x7498u, 0x74CAu, 0x7506u, - 0x7524u, 0x753Bu, 0x753Eu, 0x7559u, 0x7565u, 0x7570u, 0x75E2u, 0x7610u, - 0x761Du, 0x761Fu, 0x7642u, 0x7669u, 0x76CAu, 0x76DBu, 0x76E7u, 0x76F4u, - 0x7701u, 0x771Eu, 0x771Fu, 0x7740u, 0x774Au, 0x778Bu, 0x77A7u, 0x784Eu, - 0x786Bu, 0x788Cu, 0x7891u, 0x78CAu, 0x78CCu, 0x78FBu, 0x792Au, 0x793Cu, - 0x793Eu, 0x7948u, 0x7949u, 0x7950u, 0x7956u, 0x795Du, 0x795Eu, 0x7965u, - 0x797Fu, 0x798Du, 0x798Eu, 0x798Fu, 0x79AEu, 0x79CAu, 0x79EBu, 0x7A1Cu, - 0x7A40u, 0x7A4Au, 0x7A4Fu, 0x7A81u, 0x7AB1u, 0x7ACBu, 0x7AEEu, 0x7B20u, - 0x7BC0u, 0x7BC6u, 0x7BC9u, 0x7C3Eu, 0x7C60u, 0x7C7Bu, 0x7C92u, 0x7CBEu, - 0x7CD2u, 0x7CD6u, 0x7CE3u, 0x7CE7u, 0x7CE8u, 0x7D00u, 0x7D10u, 0x7D22u, - 0x7D2Fu, 0x7D5Bu, 0x7D63u, 0x7DA0u, 0x7DBEu, 0x7DC7u, 0x7DF4u, 0x7E02u, - 0x7E09u, 0x7E37u, 0x7E41u, 0x7E45u, 0x7F3Eu, 0x7F72u, 0x7F79u, 0x7F7Au, - 0x7F85u, 0x7F95u, 0x7F9Au, 0x7FBDu, 0x7FFAu, 0x8001u, 0x8005u, 0x8046u, - 0x8060u, 0x806Fu, 0x8070u, 0x807Eu, 0x808Bu, 0x80ADu, 0x80B2u, 0x8103u, - 0x813Eu, 0x81D8u, 0x81E8u, 0x81EDu, 0x8201u, 0x8204u, 0x8218u, 0x826Fu, - 0x8279u, 0x828Bu, 0x8291u, 0x829Du, 0x82B1u, 0x82B3u, 0x82BDu, 0x82E5u, - 0x82E6u, 0x831Du, 0x8323u, 0x8336u, 0x8352u, 0x8353u, 0x8363u, 0x83ADu, - 0x83BDu, 0x83C9u, 0x83CAu, 0x83CCu, 0x83DCu, 0x83E7u, 0x83EFu, 0x83F1u, - 0x843Du, 0x8449u, 0x8457u, 0x84EEu, 0x84F1u, 0x84F3u, 0x84FCu, 0x8516u, - 0x8564u, 0x85CDu, 0x85FAu, 0x8606u, 0x8612u, 0x862Du, 0x863Fu, 0x8650u, - 0x865Cu, 0x8667u, 0x8669u, 0x8688u, 0x86A9u, 0x86E2u, 0x870Eu, 0x8728u, - 0x876Bu, 0x8779u, 0x8786u, 0x87BAu, 0x87E1u, 0x8801u, 0x881Fu, 0x884Cu, - 0x8860u, 0x8863u, 0x88C2u, 0x88CFu, 0x88D7u, 0x88DEu, 0x88E1u, 0x88F8u, - 0x88FAu, 0x8910u, 0x8941u, 0x8964u, 0x8986u, 0x898Bu, 0x8996u, 0x8AA0u, - 0x8AAAu, 0x8ABFu, 0x8ACBu, 0x8AD2u, 0x8AD6u, 0x8AEDu, 0x8AF8u, 0x8AFEu, - 0x8B01u, 0x8B39u, 0x8B58u, 0x8B80u, 0x8B8Au, 0x8C48u, 0x8C55u, 0x8CABu, - 0x8CC1u, 0x8CC2u, 0x8CC8u, 0x8CD3u, 0x8D08u, 0x8D1Bu, 0x8D77u, 0x8DBCu, - 0x8DCBu, 0x8DEFu, 0x8DF0u, 0x8ECAu, 0x8ED4u, 0x8F26u, 0x8F2Au, 0x8F38u, - 0x8F3Bu, 0x8F62u, 0x8F9Eu, 0x8FB0u, 0x8FB6u, 0x9023u, 0x9038u, 0x9072u, - 0x907Cu, 0x908Fu, 0x9094u, 0x90CEu, 0x90DEu, 0x90F1u, 0x90FDu, 0x9111u, - 0x911Bu, 0x916Au, 0x9199u, 0x91B4u, 0x91CCu, 0x91CFu, 0x91D1u, 0x9234u, - 0x9238u, 0x9276u, 0x927Cu, 0x92D7u, 0x92D8u, 0x9304u, 0x934Au, 0x93F9u, - 0x9415u, 0x958Bu, 0x95ADu, 0x95B7u, 0x962Eu, 0x964Bu, 0x964Du, 0x9675u, - 0x9678u, 0x967Cu, 0x9686u, 0x96A3u, 0x96B7u, 0x96B8u, 0x96C3u, 0x96E2u, - 0x96E3u, 0x96F6u, 0x96F7u, 0x9723u, 0x9732u, 0x9748u, 0x9756u, 0x97DBu, - 0x97E0u, 0x97FFu, 0x980Bu, 0x9818u, 0x9829u, 0x983Bu, 0x985Eu, 0x98E2u, - 0x98EFu, 0x98FCu, 0x9928u, 0x9929u, 0x99A7u, 0x99C2u, 0x99F1u, 0x99FEu, - 0x9A6Au, 0x9B12u, 0x9B6Fu, 0x9C40u, 0x9C57u, 0x9CFDu, 0x9D67u, 0x9DB4u, - 0x9DFAu, 0x9E1Eu, 0x9E7Fu, 0x9E97u, 0x9E9Fu, 0x9EBBu, 0x9ECEu, 0x9EF9u, - 0x9EFEu, 0x9F05u, 0x9F0Fu, 0x9F16u, 0x9F3Bu, 0x9F43u, 0x9F8Du, 0x9F8Eu, - 0x9F9Cu, + 0x003B, 0x004B, 0x0060, 0x00B4, 0x00B7, 0x00C5, 0x02B9, 0x0300, + 0x0301, 0x0313, 0x0385, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, + 0x038E, 0x038F, 0x0390, 0x03A9, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B9, 0x03CC, 0x03CD, 0x03CE, 0x2002, 0x2003, 0x3008, + 0x3009, 0x349E, 0x34B9, 0x34BB, 0x34DF, 0x3515, 0x36EE, 0x36FC, + 0x3781, 0x382F, 0x3862, 0x387C, 0x38C7, 0x38E3, 0x391C, 0x393A, + 0x3A2E, 0x3A6C, 0x3AE4, 0x3B08, 0x3B19, 0x3B49, 0x3B9D, 0x3C18, + 0x3C4E, 0x3D33, 0x3D96, 0x3EAC, 0x3EB8, 0x3F1B, 0x3FFC, 0x4008, + 0x4018, 0x4039, 0x4046, 0x4096, 0x40E3, 0x412F, 0x4202, 0x4227, + 0x42A0, 0x4301, 0x4334, 0x4359, 0x43D5, 0x43D9, 0x440B, 0x446B, + 0x452B, 0x455D, 0x4561, 0x456B, 0x45D7, 0x45F9, 0x4635, 0x46BE, + 0x46C7, 0x4995, 0x49E6, 0x4A6E, 0x4A76, 0x4AB2, 0x4B33, 0x4BCE, + 0x4CCE, 0x4CED, 0x4CF8, 0x4D56, 0x4E0D, 0x4E26, 0x4E32, 0x4E38, + 0x4E39, 0x4E3D, 0x4E41, 0x4E82, 0x4E86, 0x4EAE, 0x4EC0, 0x4ECC, + 0x4EE4, 0x4F60, 0x4F80, 0x4F86, 0x4F8B, 0x4FAE, 0x4FBB, 0x4FBF, + 0x5002, 0x502B, 0x507A, 0x5099, 0x50CF, 0x50DA, 0x50E7, 0x5140, + 0x5145, 0x514D, 0x5154, 0x5164, 0x5167, 0x5168, 0x5169, 0x516D, + 0x5177, 0x5180, 0x518D, 0x5192, 0x5195, 0x5197, 0x51A4, 0x51AC, + 0x51B5, 0x51B7, 0x51C9, 0x51CC, 0x51DC, 0x51DE, 0x51F5, 0x5203, + 0x5207, 0x5217, 0x5229, 0x523A, 0x523B, 0x5246, 0x5272, 0x5277, + 0x5289, 0x529B, 0x52A3, 0x52B3, 0x52C7, 0x52C9, 0x52D2, 0x52DE, + 0x52E4, 0x52F5, 0x52FA, 0x5305, 0x5306, 0x5317, 0x533F, 0x5349, + 0x5351, 0x535A, 0x5373, 0x5375, 0x537D, 0x537F, 0x53C3, 0x53CA, + 0x53DF, 0x53E5, 0x53EB, 0x53F1, 0x5406, 0x540F, 0x541D, 0x5438, + 0x5442, 0x5448, 0x5468, 0x549E, 0x54A2, 0x54BD, 0x54F6, 0x5510, + 0x5553, 0x5555, 0x5563, 0x5584, 0x5587, 0x5599, 0x559D, 0x55AB, + 0x55B3, 0x55C0, 0x55C2, 0x55E2, 0x5606, 0x5651, 0x5668, 0x5674, + 0x56F9, 0x5716, 0x5717, 0x578B, 0x57CE, 0x57F4, 0x580D, 0x5831, + 0x5832, 0x5840, 0x585A, 0x585E, 0x58A8, 0x58AC, 0x58B3, 0x58D8, + 0x58DF, 0x58EE, 0x58F2, 0x58F7, 0x5906, 0x591A, 0x5922, 0x5944, + 0x5948, 0x5951, 0x5954, 0x5962, 0x5973, 0x59D8, 0x59EC, 0x5A1B, + 0x5A27, 0x5A62, 0x5A66, 0x5AB5, 0x5B08, 0x5B28, 0x5B3E, 0x5B85, + 0x5BC3, 0x5BD8, 0x5BE7, 0x5BEE, 0x5BF3, 0x5BFF, 0x5C06, 0x5C22, + 0x5C3F, 0x5C60, 0x5C62, 0x5C64, 0x5C65, 0x5C6E, 0x5C8D, 0x5CC0, + 0x5D19, 0x5D43, 0x5D50, 0x5D6B, 0x5D6E, 0x5D7C, 0x5DB2, 0x5DBA, + 0x5DE1, 0x5DE2, 0x5DFD, 0x5E28, 0x5E3D, 0x5E69, 0x5E74, 0x5EA6, + 0x5EB0, 0x5EB3, 0x5EB6, 0x5EC9, 0x5ECA, 0x5ED2, 0x5ED3, 0x5ED9, + 0x5EEC, 0x5EFE, 0x5F04, 0x5F22, 0x5F53, 0x5F62, 0x5F69, 0x5F6B, + 0x5F8B, 0x5F9A, 0x5FA9, 0x5FAD, 0x5FCD, 0x5FD7, 0x5FF5, 0x5FF9, + 0x6012, 0x601C, 0x6075, 0x6081, 0x6094, 0x60C7, 0x60D8, 0x60E1, + 0x6108, 0x6144, 0x6148, 0x614C, 0x614E, 0x6160, 0x6168, 0x617A, + 0x618E, 0x6190, 0x61A4, 0x61AF, 0x61B2, 0x61DE, 0x61F2, 0x61F6, + 0x6200, 0x6210, 0x621B, 0x622E, 0x6234, 0x625D, 0x62B1, 0x62C9, + 0x62CF, 0x62D3, 0x62D4, 0x62FC, 0x62FE, 0x633D, 0x6350, 0x6368, + 0x637B, 0x6383, 0x63A0, 0x63A9, 0x63C4, 0x63C5, 0x63E4, 0x641C, + 0x6422, 0x6452, 0x6469, 0x6477, 0x647E, 0x649A, 0x649D, 0x64C4, + 0x654F, 0x6556, 0x656C, 0x6578, 0x6599, 0x65C5, 0x65E2, 0x65E3, + 0x6613, 0x6649, 0x6674, 0x6688, 0x6691, 0x669C, 0x66B4, 0x66C6, + 0x66F4, 0x66F8, 0x6700, 0x6717, 0x671B, 0x6721, 0x674E, 0x6753, + 0x6756, 0x675E, 0x677B, 0x6785, 0x6797, 0x67F3, 0x67FA, 0x6817, + 0x681F, 0x6852, 0x6881, 0x6885, 0x688E, 0x68A8, 0x6914, 0x6942, + 0x69A3, 0x69EA, 0x6A02, 0x6A13, 0x6AA8, 0x6AD3, 0x6ADB, 0x6B04, + 0x6B21, 0x6B54, 0x6B72, 0x6B77, 0x6B79, 0x6B9F, 0x6BAE, 0x6BBA, + 0x6BBB, 0x6C4E, 0x6C67, 0x6C88, 0x6CBF, 0x6CCC, 0x6CCD, 0x6CE5, + 0x6D16, 0x6D1B, 0x6D1E, 0x6D34, 0x6D3E, 0x6D41, 0x6D69, 0x6D6A, + 0x6D77, 0x6D78, 0x6D85, 0x6DCB, 0x6DDA, 0x6DEA, 0x6DF9, 0x6E1A, + 0x6E2F, 0x6E6E, 0x6E9C, 0x6EBA, 0x6EC7, 0x6ECB, 0x6ED1, 0x6EDB, + 0x6F0F, 0x6F22, 0x6F23, 0x6F6E, 0x6FC6, 0x6FEB, 0x6FFE, 0x701B, + 0x701E, 0x7039, 0x704A, 0x7070, 0x7077, 0x707D, 0x7099, 0x70AD, + 0x70C8, 0x70D9, 0x7145, 0x7149, 0x716E, 0x719C, 0x71CE, 0x71D0, + 0x7210, 0x721B, 0x7228, 0x722B, 0x7235, 0x7250, 0x7262, 0x7280, + 0x7295, 0x72AF, 0x72C0, 0x72FC, 0x732A, 0x7375, 0x737A, 0x7387, + 0x738B, 0x73A5, 0x73B2, 0x73DE, 0x7406, 0x7409, 0x7422, 0x7447, + 0x745C, 0x7469, 0x7471, 0x7485, 0x7489, 0x7498, 0x74CA, 0x7506, + 0x7524, 0x753B, 0x753E, 0x7559, 0x7565, 0x7570, 0x75E2, 0x7610, + 0x761D, 0x761F, 0x7642, 0x7669, 0x76CA, 0x76DB, 0x76E7, 0x76F4, + 0x7701, 0x771E, 0x771F, 0x7740, 0x774A, 0x778B, 0x77A7, 0x784E, + 0x786B, 0x788C, 0x7891, 0x78CA, 0x78CC, 0x78FB, 0x792A, 0x793C, + 0x793E, 0x7948, 0x7949, 0x7950, 0x7956, 0x795D, 0x795E, 0x7965, + 0x797F, 0x798D, 0x798E, 0x798F, 0x79AE, 0x79CA, 0x79EB, 0x7A1C, + 0x7A40, 0x7A4A, 0x7A4F, 0x7A81, 0x7AB1, 0x7ACB, 0x7AEE, 0x7B20, + 0x7BC0, 0x7BC6, 0x7BC9, 0x7C3E, 0x7C60, 0x7C7B, 0x7C92, 0x7CBE, + 0x7CD2, 0x7CD6, 0x7CE3, 0x7CE7, 0x7CE8, 0x7D00, 0x7D10, 0x7D22, + 0x7D2F, 0x7D5B, 0x7D63, 0x7DA0, 0x7DBE, 0x7DC7, 0x7DF4, 0x7E02, + 0x7E09, 0x7E37, 0x7E41, 0x7E45, 0x7F3E, 0x7F72, 0x7F79, 0x7F7A, + 0x7F85, 0x7F95, 0x7F9A, 0x7FBD, 0x7FFA, 0x8001, 0x8005, 0x8046, + 0x8060, 0x806F, 0x8070, 0x807E, 0x808B, 0x80AD, 0x80B2, 0x8103, + 0x813E, 0x81D8, 0x81E8, 0x81ED, 0x8201, 0x8204, 0x8218, 0x826F, + 0x8279, 0x828B, 0x8291, 0x829D, 0x82B1, 0x82B3, 0x82BD, 0x82E5, + 0x82E6, 0x831D, 0x8323, 0x8336, 0x8352, 0x8353, 0x8363, 0x83AD, + 0x83BD, 0x83C9, 0x83CA, 0x83CC, 0x83DC, 0x83E7, 0x83EF, 0x83F1, + 0x843D, 0x8449, 0x8457, 0x84EE, 0x84F1, 0x84F3, 0x84FC, 0x8516, + 0x8564, 0x85CD, 0x85FA, 0x8606, 0x8612, 0x862D, 0x863F, 0x8650, + 0x865C, 0x8667, 0x8669, 0x8688, 0x86A9, 0x86E2, 0x870E, 0x8728, + 0x876B, 0x8779, 0x8786, 0x87BA, 0x87E1, 0x8801, 0x881F, 0x884C, + 0x8860, 0x8863, 0x88C2, 0x88CF, 0x88D7, 0x88DE, 0x88E1, 0x88F8, + 0x88FA, 0x8910, 0x8941, 0x8964, 0x8986, 0x898B, 0x8996, 0x8AA0, + 0x8AAA, 0x8ABF, 0x8ACB, 0x8AD2, 0x8AD6, 0x8AED, 0x8AF8, 0x8AFE, + 0x8B01, 0x8B39, 0x8B58, 0x8B80, 0x8B8A, 0x8C48, 0x8C55, 0x8CAB, + 0x8CC1, 0x8CC2, 0x8CC8, 0x8CD3, 0x8D08, 0x8D1B, 0x8D77, 0x8DBC, + 0x8DCB, 0x8DEF, 0x8DF0, 0x8ECA, 0x8ED4, 0x8F26, 0x8F2A, 0x8F38, + 0x8F3B, 0x8F62, 0x8F9E, 0x8FB0, 0x8FB6, 0x9023, 0x9038, 0x9072, + 0x907C, 0x908F, 0x9094, 0x90CE, 0x90DE, 0x90F1, 0x90FD, 0x9111, + 0x911B, 0x916A, 0x9199, 0x91B4, 0x91CC, 0x91CF, 0x91D1, 0x9234, + 0x9238, 0x9276, 0x927C, 0x92D7, 0x92D8, 0x9304, 0x934A, 0x93F9, + 0x9415, 0x958B, 0x95AD, 0x95B7, 0x962E, 0x964B, 0x964D, 0x9675, + 0x9678, 0x967C, 0x9686, 0x96A3, 0x96B7, 0x96B8, 0x96C3, 0x96E2, + 0x96E3, 0x96F6, 0x96F7, 0x9723, 0x9732, 0x9748, 0x9756, 0x97DB, + 0x97E0, 0x97FF, 0x980B, 0x9818, 0x9829, 0x983B, 0x985E, 0x98E2, + 0x98EF, 0x98FC, 0x9928, 0x9929, 0x99A7, 0x99C2, 0x99F1, 0x99FE, + 0x9A6A, 0x9B12, 0x9B6F, 0x9C40, 0x9C57, 0x9CFD, 0x9D67, 0x9DB4, + 0x9DFA, 0x9E1E, 0x9E7F, 0x9E97, 0x9E9F, 0x9EBB, 0x9ECE, 0x9EF9, + 0x9EFE, 0x9F05, 0x9F0F, 0x9F16, 0x9F3B, 0x9F43, 0x9F8D, 0x9F8E, + 0x9F9C, }; -static const uint16_t -_hb_ucd_dm1_p2_map[110] = +static const uint16_t _hb_ucd_dm1_p2_map[110]= { - 0x0122u, 0x051Cu, 0x0525u, 0x054Bu, 0x063Au, 0x0804u, 0x08DEu, 0x0A2Cu, - 0x0B63u, 0x14E4u, 0x16A8u, 0x16EAu, 0x19C8u, 0x1B18u, 0x1D0Bu, 0x1DE4u, - 0x1DE6u, 0x2183u, 0x219Fu, 0x2331u, 0x26D4u, 0x2844u, 0x284Au, 0x2B0Cu, - 0x2BF1u, 0x300Au, 0x32B8u, 0x335Fu, 0x3393u, 0x339Cu, 0x33C3u, 0x33D5u, - 0x346Du, 0x36A3u, 0x38A7u, 0x3A8Du, 0x3AFAu, 0x3CBCu, 0x3D1Eu, 0x3ED1u, - 0x3F5Eu, 0x3F8Eu, 0x4263u, 0x42EEu, 0x43ABu, 0x4608u, 0x4735u, 0x4814u, - 0x4C36u, 0x4C92u, 0x4FA1u, 0x4FB8u, 0x5044u, 0x50F2u, 0x50F3u, 0x5119u, - 0x5133u, 0x5249u, 0x541Du, 0x5626u, 0x569Au, 0x56C5u, 0x597Cu, 0x5AA7u, - 0x5BABu, 0x5C80u, 0x5CD0u, 0x5F86u, 0x61DAu, 0x6228u, 0x6247u, 0x62D9u, - 0x633Eu, 0x64DAu, 0x6523u, 0x65A8u, 0x67A7u, 0x67B5u, 0x6B3Cu, 0x6C36u, - 0x6CD5u, 0x6D6Bu, 0x6F2Cu, 0x6FB1u, 0x70D2u, 0x73CAu, 0x7667u, 0x78AEu, - 0x7966u, 0x7CA8u, 0x7ED3u, 0x7F2Fu, 0x85D2u, 0x85EDu, 0x872Eu, 0x8BFAu, - 0x8D77u, 0x9145u, 0x91DFu, 0x921Au, 0x940Au, 0x9496u, 0x95B6u, 0x9B30u, - 0xA0CEu, 0xA105u, 0xA20Eu, 0xA291u, 0xA392u, 0xA600u, + 0x0122, 0x051C, 0x0525, 0x054B, 0x063A, 0x0804, 0x08DE, 0x0A2C, + 0x0B63, 0x14E4, 0x16A8, 0x16EA, 0x19C8, 0x1B18, 0x1D0B, 0x1DE4, + 0x1DE6, 0x2183, 0x219F, 0x2331, 0x26D4, 0x2844, 0x284A, 0x2B0C, + 0x2BF1, 0x300A, 0x32B8, 0x335F, 0x3393, 0x339C, 0x33C3, 0x33D5, + 0x346D, 0x36A3, 0x38A7, 0x3A8D, 0x3AFA, 0x3CBC, 0x3D1E, 0x3ED1, + 0x3F5E, 0x3F8E, 0x4263, 0x42EE, 0x43AB, 0x4608, 0x4735, 0x4814, + 0x4C36, 0x4C92, 0x4FA1, 0x4FB8, 0x5044, 0x50F2, 0x50F3, 0x5119, + 0x5133, 0x5249, 0x541D, 0x5626, 0x569A, 0x56C5, 0x597C, 0x5AA7, + 0x5BAB, 0x5C80, 0x5CD0, 0x5F86, 0x61DA, 0x6228, 0x6247, 0x62D9, + 0x633E, 0x64DA, 0x6523, 0x65A8, 0x67A7, 0x67B5, 0x6B3C, 0x6C36, + 0x6CD5, 0x6D6B, 0x6F2C, 0x6FB1, 0x70D2, 0x73CA, 0x7667, 0x78AE, + 0x7966, 0x7CA8, 0x7ED3, 0x7F2F, 0x85D2, 0x85ED, 0x872E, 0x8BFA, + 0x8D77, 0x9145, 0x91DF, 0x921A, 0x940A, 0x9496, 0x95B6, 0x9B30, + 0xA0CE, 0xA105, 0xA20E, 0xA291, 0xA392, 0xA600, }; -static const uint32_t -_hb_ucd_dm2_u32_map[638] = +static const uint32_t _hb_ucd_dm2_u32_map[638]= { - HB_CODEPOINT_ENCODE3_11_7_14 (0x003Cu, 0x0338u, 0x226Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x003Du, 0x0338u, 0x2260u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x003Eu, 0x0338u, 0x226Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0300u, 0x00C0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0301u, 0x00C1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0302u, 0x00C2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0303u, 0x00C3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0304u, 0x0100u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0306u, 0x0102u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0307u, 0x0226u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0308u, 0x00C4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0309u, 0x1EA2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Au, 0x00C5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Cu, 0x01CDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Fu, 0x0200u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0311u, 0x0202u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0323u, 0x1EA0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0325u, 0x1E00u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0328u, 0x0104u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0307u, 0x1E02u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0323u, 0x1E04u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0331u, 0x1E06u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0301u, 0x0106u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0302u, 0x0108u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0307u, 0x010Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x030Cu, 0x010Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0327u, 0x00C7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0307u, 0x1E0Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x030Cu, 0x010Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0323u, 0x1E0Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0327u, 0x1E10u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x032Du, 0x1E12u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0331u, 0x1E0Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0300u, 0x00C8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0301u, 0x00C9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0302u, 0x00CAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0303u, 0x1EBCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0304u, 0x0112u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0306u, 0x0114u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0307u, 0x0116u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0308u, 0x00CBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0309u, 0x1EBAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Cu, 0x011Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Fu, 0x0204u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0311u, 0x0206u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0323u, 0x1EB8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0327u, 0x0228u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0328u, 0x0118u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x032Du, 0x1E18u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0330u, 0x1E1Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0046u, 0x0307u, 0x1E1Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0301u, 0x01F4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0302u, 0x011Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0304u, 0x1E20u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0306u, 0x011Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0307u, 0x0120u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x030Cu, 0x01E6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0327u, 0x0122u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0302u, 0x0124u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0307u, 0x1E22u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0308u, 0x1E26u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x030Cu, 0x021Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0323u, 0x1E24u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0327u, 0x1E28u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x032Eu, 0x1E2Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0300u, 0x00CCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0301u, 0x00CDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0302u, 0x00CEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0303u, 0x0128u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0304u, 0x012Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0306u, 0x012Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0307u, 0x0130u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0308u, 0x00CFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0309u, 0x1EC8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Cu, 0x01CFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Fu, 0x0208u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0311u, 0x020Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0323u, 0x1ECAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0328u, 0x012Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0330u, 0x1E2Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Au, 0x0302u, 0x0134u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0301u, 0x1E30u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x030Cu, 0x01E8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0323u, 0x1E32u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0327u, 0x0136u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0331u, 0x1E34u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0301u, 0x0139u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x030Cu, 0x013Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0323u, 0x1E36u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0327u, 0x013Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x032Du, 0x1E3Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0331u, 0x1E3Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0301u, 0x1E3Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0307u, 0x1E40u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0323u, 0x1E42u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0300u, 0x01F8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0301u, 0x0143u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0303u, 0x00D1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0307u, 0x1E44u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x030Cu, 0x0147u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0323u, 0x1E46u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0327u, 0x0145u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x032Du, 0x1E4Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0331u, 0x1E48u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0300u, 0x00D2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0301u, 0x00D3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0302u, 0x00D4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0303u, 0x00D5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0304u, 0x014Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0306u, 0x014Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0307u, 0x022Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0308u, 0x00D6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0309u, 0x1ECEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Bu, 0x0150u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Cu, 0x01D1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Fu, 0x020Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0311u, 0x020Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x031Bu, 0x01A0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0323u, 0x1ECCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0328u, 0x01EAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0301u, 0x1E54u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0307u, 0x1E56u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0301u, 0x0154u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0307u, 0x1E58u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Cu, 0x0158u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Fu, 0x0210u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0311u, 0x0212u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0323u, 0x1E5Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0327u, 0x0156u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0331u, 0x1E5Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0301u, 0x015Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0302u, 0x015Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0307u, 0x1E60u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x030Cu, 0x0160u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0323u, 0x1E62u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0326u, 0x0218u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0327u, 0x015Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0307u, 0x1E6Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x030Cu, 0x0164u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0323u, 0x1E6Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0326u, 0x021Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0327u, 0x0162u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x032Du, 0x1E70u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0331u, 0x1E6Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0300u, 0x00D9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0301u, 0x00DAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0302u, 0x00DBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0303u, 0x0168u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0304u, 0x016Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0306u, 0x016Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0308u, 0x00DCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0309u, 0x1EE6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Au, 0x016Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Bu, 0x0170u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Cu, 0x01D3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Fu, 0x0214u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0311u, 0x0216u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x031Bu, 0x01AFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0323u, 0x1EE4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0324u, 0x1E72u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0328u, 0x0172u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x032Du, 0x1E76u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0330u, 0x1E74u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0303u, 0x1E7Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0323u, 0x1E7Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0300u, 0x1E80u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0301u, 0x1E82u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0302u, 0x0174u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0307u, 0x1E86u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0308u, 0x1E84u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0323u, 0x1E88u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0307u, 0x1E8Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0308u, 0x1E8Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0300u, 0x1EF2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0301u, 0x00DDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0302u, 0x0176u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0303u, 0x1EF8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0304u, 0x0232u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0307u, 0x1E8Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0308u, 0x0178u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0309u, 0x1EF6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0323u, 0x1EF4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0301u, 0x0179u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0302u, 0x1E90u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0307u, 0x017Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x030Cu, 0x017Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0323u, 0x1E92u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0331u, 0x1E94u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0300u, 0x00E0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0301u, 0x00E1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0302u, 0x00E2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0303u, 0x00E3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0304u, 0x0101u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0306u, 0x0103u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0307u, 0x0227u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0308u, 0x00E4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0309u, 0x1EA3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Au, 0x00E5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Cu, 0x01CEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Fu, 0x0201u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0311u, 0x0203u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0323u, 0x1EA1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0325u, 0x1E01u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0328u, 0x0105u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0307u, 0x1E03u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0323u, 0x1E05u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0331u, 0x1E07u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0301u, 0x0107u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0302u, 0x0109u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0307u, 0x010Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x030Cu, 0x010Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0327u, 0x00E7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0307u, 0x1E0Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x030Cu, 0x010Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0323u, 0x1E0Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0327u, 0x1E11u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x032Du, 0x1E13u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0331u, 0x1E0Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0300u, 0x00E8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0301u, 0x00E9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0302u, 0x00EAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0303u, 0x1EBDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0304u, 0x0113u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0306u, 0x0115u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0307u, 0x0117u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0308u, 0x00EBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0309u, 0x1EBBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Cu, 0x011Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Fu, 0x0205u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0311u, 0x0207u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0323u, 0x1EB9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0327u, 0x0229u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0328u, 0x0119u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x032Du, 0x1E19u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0330u, 0x1E1Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0066u, 0x0307u, 0x1E1Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0301u, 0x01F5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0302u, 0x011Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0304u, 0x1E21u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0306u, 0x011Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0307u, 0x0121u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x030Cu, 0x01E7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0327u, 0x0123u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0302u, 0x0125u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0307u, 0x1E23u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0308u, 0x1E27u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x030Cu, 0x021Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0323u, 0x1E25u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0327u, 0x1E29u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x032Eu, 0x1E2Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0331u, 0x1E96u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0300u, 0x00ECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0301u, 0x00EDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0302u, 0x00EEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0303u, 0x0129u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0304u, 0x012Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0306u, 0x012Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0308u, 0x00EFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0309u, 0x1EC9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Cu, 0x01D0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Fu, 0x0209u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0311u, 0x020Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0323u, 0x1ECBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0328u, 0x012Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0330u, 0x1E2Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x0302u, 0x0135u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x030Cu, 0x01F0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0301u, 0x1E31u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x030Cu, 0x01E9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0323u, 0x1E33u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0327u, 0x0137u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0331u, 0x1E35u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0301u, 0x013Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x030Cu, 0x013Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0323u, 0x1E37u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0327u, 0x013Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x032Du, 0x1E3Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0331u, 0x1E3Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0301u, 0x1E3Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0307u, 0x1E41u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0323u, 0x1E43u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0300u, 0x01F9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0301u, 0x0144u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0303u, 0x00F1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0307u, 0x1E45u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x030Cu, 0x0148u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0323u, 0x1E47u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0327u, 0x0146u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x032Du, 0x1E4Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0331u, 0x1E49u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0300u, 0x00F2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0301u, 0x00F3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0302u, 0x00F4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0303u, 0x00F5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0304u, 0x014Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0306u, 0x014Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0307u, 0x022Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0308u, 0x00F6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0309u, 0x1ECFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Bu, 0x0151u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Cu, 0x01D2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Fu, 0x020Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0311u, 0x020Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x031Bu, 0x01A1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0323u, 0x1ECDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0328u, 0x01EBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0301u, 0x1E55u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0307u, 0x1E57u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0301u, 0x0155u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0307u, 0x1E59u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Cu, 0x0159u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Fu, 0x0211u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0311u, 0x0213u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0323u, 0x1E5Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0327u, 0x0157u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0331u, 0x1E5Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0301u, 0x015Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0302u, 0x015Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0307u, 0x1E61u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x030Cu, 0x0161u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0323u, 0x1E63u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0326u, 0x0219u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0327u, 0x015Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0307u, 0x1E6Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0308u, 0x1E97u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x030Cu, 0x0165u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0323u, 0x1E6Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0326u, 0x021Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0327u, 0x0163u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x032Du, 0x1E71u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0331u, 0x1E6Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0300u, 0x00F9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0301u, 0x00FAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0302u, 0x00FBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0303u, 0x0169u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0304u, 0x016Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0306u, 0x016Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0308u, 0x00FCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0309u, 0x1EE7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Au, 0x016Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Bu, 0x0171u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Cu, 0x01D4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Fu, 0x0215u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0311u, 0x0217u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x031Bu, 0x01B0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0323u, 0x1EE5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0324u, 0x1E73u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0328u, 0x0173u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x032Du, 0x1E77u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0330u, 0x1E75u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0303u, 0x1E7Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0323u, 0x1E7Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0300u, 0x1E81u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0301u, 0x1E83u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0302u, 0x0175u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0307u, 0x1E87u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0308u, 0x1E85u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x030Au, 0x1E98u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0323u, 0x1E89u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0307u, 0x1E8Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0308u, 0x1E8Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0300u, 0x1EF3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0301u, 0x00FDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0302u, 0x0177u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0303u, 0x1EF9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0304u, 0x0233u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0307u, 0x1E8Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0308u, 0x00FFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0309u, 0x1EF7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x030Au, 0x1E99u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0323u, 0x1EF5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0301u, 0x017Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0302u, 0x1E91u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0307u, 0x017Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x030Cu, 0x017Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0323u, 0x1E93u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0331u, 0x1E95u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0300u, 0x1FEDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0301u, 0x0385u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0342u, 0x1FC1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0300u, 0x1EA6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0301u, 0x1EA4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0303u, 0x1EAAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0309u, 0x1EA8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C4u, 0x0304u, 0x01DEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C5u, 0x0301u, 0x01FAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0301u, 0x01FCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0304u, 0x01E2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00C7u, 0x0301u, 0x1E08u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0300u, 0x1EC0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0301u, 0x1EBEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0303u, 0x1EC4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0309u, 0x1EC2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00CFu, 0x0301u, 0x1E2Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0300u, 0x1ED2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0301u, 0x1ED0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0303u, 0x1ED6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0309u, 0x1ED4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0301u, 0x1E4Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0304u, 0x022Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0308u, 0x1E4Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D6u, 0x0304u, 0x022Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00D8u, 0x0301u, 0x01FEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0300u, 0x01DBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0301u, 0x01D7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0304u, 0x01D5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x030Cu, 0x01D9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0300u, 0x1EA7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0301u, 0x1EA5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0303u, 0x1EABu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0309u, 0x1EA9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E4u, 0x0304u, 0x01DFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E5u, 0x0301u, 0x01FBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0301u, 0x01FDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0304u, 0x01E3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00E7u, 0x0301u, 0x1E09u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0300u, 0x1EC1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0301u, 0x1EBFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0303u, 0x1EC5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0309u, 0x1EC3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00EFu, 0x0301u, 0x1E2Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0300u, 0x1ED3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0301u, 0x1ED1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0303u, 0x1ED7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0309u, 0x1ED5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0301u, 0x1E4Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0304u, 0x022Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0308u, 0x1E4Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F6u, 0x0304u, 0x022Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00F8u, 0x0301u, 0x01FFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0300u, 0x01DCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0301u, 0x01D8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0304u, 0x01D6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x030Cu, 0x01DAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0300u, 0x1EB0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0301u, 0x1EAEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0303u, 0x1EB4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0309u, 0x1EB2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0300u, 0x1EB1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0301u, 0x1EAFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0303u, 0x1EB5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0309u, 0x1EB3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0300u, 0x1E14u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0301u, 0x1E16u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0300u, 0x1E15u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0301u, 0x1E17u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0300u, 0x1E50u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0301u, 0x1E52u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0300u, 0x1E51u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0301u, 0x1E53u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x015Au, 0x0307u, 0x1E64u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x015Bu, 0x0307u, 0x1E65u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0160u, 0x0307u, 0x1E66u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0161u, 0x0307u, 0x1E67u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0168u, 0x0301u, 0x1E78u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0169u, 0x0301u, 0x1E79u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x016Au, 0x0308u, 0x1E7Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x016Bu, 0x0308u, 0x1E7Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x017Fu, 0x0307u, 0x1E9Bu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0300u, 0x1EDCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0301u, 0x1EDAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0303u, 0x1EE0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0309u, 0x1EDEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0323u, 0x1EE2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0300u, 0x1EDDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0301u, 0x1EDBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0303u, 0x1EE1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0309u, 0x1EDFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0323u, 0x1EE3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0300u, 0x1EEAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0301u, 0x1EE8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0303u, 0x1EEEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0309u, 0x1EECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0323u, 0x1EF0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0300u, 0x1EEBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0301u, 0x1EE9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0303u, 0x1EEFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0309u, 0x1EEDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0323u, 0x1EF1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01B7u, 0x030Cu, 0x01EEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01EAu, 0x0304u, 0x01ECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x01EBu, 0x0304u, 0x01EDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0226u, 0x0304u, 0x01E0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0227u, 0x0304u, 0x01E1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0228u, 0x0306u, 0x1E1Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0229u, 0x0306u, 0x1E1Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x022Eu, 0x0304u, 0x0230u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x022Fu, 0x0304u, 0x0231u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0292u, 0x030Cu, 0x01EFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0308u, 0x0301u, 0x0000u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0300u, 0x1FBAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0301u, 0x0386u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0304u, 0x1FB9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0306u, 0x1FB8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0313u, 0x1F08u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0314u, 0x1F09u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0345u, 0x1FBCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0300u, 0x1FC8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0301u, 0x0388u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0313u, 0x1F18u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0314u, 0x1F19u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0300u, 0x1FCAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0301u, 0x0389u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0313u, 0x1F28u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0314u, 0x1F29u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0345u, 0x1FCCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0300u, 0x1FDAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0301u, 0x038Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0304u, 0x1FD9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0306u, 0x1FD8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0308u, 0x03AAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0313u, 0x1F38u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0314u, 0x1F39u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0300u, 0x1FF8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0301u, 0x038Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0313u, 0x1F48u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0314u, 0x1F49u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A1u, 0x0314u, 0x1FECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0300u, 0x1FEAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0301u, 0x038Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0304u, 0x1FE9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0306u, 0x1FE8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0308u, 0x03ABu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0314u, 0x1F59u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0300u, 0x1FFAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0301u, 0x038Fu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0313u, 0x1F68u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0314u, 0x1F69u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0345u, 0x1FFCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03ACu, 0x0345u, 0x1FB4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03AEu, 0x0345u, 0x1FC4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0300u, 0x1F70u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0301u, 0x03ACu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0304u, 0x1FB1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0306u, 0x1FB0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0313u, 0x1F00u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0314u, 0x1F01u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0342u, 0x1FB6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0345u, 0x1FB3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0300u, 0x1F72u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0301u, 0x03ADu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0313u, 0x1F10u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0314u, 0x1F11u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0300u, 0x1F74u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0301u, 0x03AEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0313u, 0x1F20u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0314u, 0x1F21u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0342u, 0x1FC6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0345u, 0x1FC3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0300u, 0x1F76u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0301u, 0x03AFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0304u, 0x1FD1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0306u, 0x1FD0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0308u, 0x03CAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0313u, 0x1F30u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0314u, 0x1F31u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0342u, 0x1FD6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0300u, 0x1F78u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0301u, 0x03CCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0313u, 0x1F40u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0314u, 0x1F41u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0313u, 0x1FE4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0314u, 0x1FE5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0300u, 0x1F7Au), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0301u, 0x03CDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0304u, 0x1FE1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0306u, 0x1FE0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0308u, 0x03CBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0313u, 0x1F50u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0314u, 0x1F51u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0342u, 0x1FE6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0300u, 0x1F7Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0301u, 0x03CEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0313u, 0x1F60u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0314u, 0x1F61u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0342u, 0x1FF6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0345u, 0x1FF3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0300u, 0x1FD2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0301u, 0x0390u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0342u, 0x1FD7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0300u, 0x1FE2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0301u, 0x03B0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0342u, 0x1FE7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03CEu, 0x0345u, 0x1FF4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0301u, 0x03D3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0308u, 0x03D4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0406u, 0x0308u, 0x0407u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0306u, 0x04D0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0308u, 0x04D2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0413u, 0x0301u, 0x0403u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0300u, 0x0400u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0306u, 0x04D6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0308u, 0x0401u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0306u, 0x04C1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0308u, 0x04DCu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0417u, 0x0308u, 0x04DEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0300u, 0x040Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0304u, 0x04E2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0306u, 0x0419u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0308u, 0x04E4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x041Au, 0x0301u, 0x040Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x041Eu, 0x0308u, 0x04E6u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0304u, 0x04EEu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0306u, 0x040Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0308u, 0x04F0u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x030Bu, 0x04F2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0427u, 0x0308u, 0x04F4u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x042Bu, 0x0308u, 0x04F8u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x042Du, 0x0308u, 0x04ECu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0306u, 0x04D1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0308u, 0x04D3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0433u, 0x0301u, 0x0453u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0300u, 0x0450u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0306u, 0x04D7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0308u, 0x0451u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0306u, 0x04C2u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0308u, 0x04DDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0437u, 0x0308u, 0x04DFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0300u, 0x045Du), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0304u, 0x04E3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0306u, 0x0439u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0308u, 0x04E5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x043Au, 0x0301u, 0x045Cu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x043Eu, 0x0308u, 0x04E7u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0304u, 0x04EFu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0306u, 0x045Eu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0308u, 0x04F1u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x030Bu, 0x04F3u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0447u, 0x0308u, 0x04F5u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x044Bu, 0x0308u, 0x04F9u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x044Du, 0x0308u, 0x04EDu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0456u, 0x0308u, 0x0457u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0474u, 0x030Fu, 0x0476u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x0475u, 0x030Fu, 0x0477u), - HB_CODEPOINT_ENCODE3_11_7_14 (0x04D8u, 0x0308u, 0x04DAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x04D9u, 0x0308u, 0x04DBu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x04E8u, 0x0308u, 0x04EAu), - HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9u, 0x0308u, 0x04EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003C, 0x0338, 0x226E),HB_CODEPOINT_ENCODE3_11_7_14 (0x003D, 0x0338, 0x2260), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003E, 0x0338, 0x226F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0300, 0x00C0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0301, 0x00C1),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0302, 0x00C2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0303, 0x00C3),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0304, 0x0100), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0306, 0x0102),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0307, 0x0226), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0308, 0x00C4),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0309, 0x1EA2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x030A, 0x00C5),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x030C, 0x01CD), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x030F, 0x0200),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0311, 0x0202), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0323, 0x1EA0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0325, 0x1E00), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041, 0x0328, 0x0104),HB_CODEPOINT_ENCODE3_11_7_14 (0x0042, 0x0307, 0x1E02), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042, 0x0323, 0x1E04),HB_CODEPOINT_ENCODE3_11_7_14 (0x0042, 0x0331, 0x1E06), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x0301, 0x0106),HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x0302, 0x0108), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x0307, 0x010A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x030C, 0x010C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043, 0x0327, 0x00C7),HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x0307, 0x1E0A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x030C, 0x010E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x0323, 0x1E0C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x0327, 0x1E10),HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x032D, 0x1E12), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044, 0x0331, 0x1E0E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0300, 0x00C8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0301, 0x00C9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0302, 0x00CA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0303, 0x1EBC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0304, 0x0112), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0306, 0x0114),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0307, 0x0116), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0308, 0x00CB),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0309, 0x1EBA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x030C, 0x011A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x030F, 0x0204), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0311, 0x0206),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0323, 0x1EB8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0327, 0x0228),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0328, 0x0118), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x032D, 0x1E18),HB_CODEPOINT_ENCODE3_11_7_14 (0x0045, 0x0330, 0x1E1A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0046, 0x0307, 0x1E1E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0301, 0x01F4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0302, 0x011C),HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0304, 0x1E20), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0306, 0x011E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0307, 0x0120), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x030C, 0x01E6),HB_CODEPOINT_ENCODE3_11_7_14 (0x0047, 0x0327, 0x0122), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0302, 0x0124),HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0307, 0x1E22), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0308, 0x1E26),HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x030C, 0x021E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0323, 0x1E24),HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x0327, 0x1E28), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048, 0x032E, 0x1E2A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0300, 0x00CC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0301, 0x00CD),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0302, 0x00CE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0303, 0x0128),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0304, 0x012A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0306, 0x012C),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0307, 0x0130), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0308, 0x00CF),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0309, 0x1EC8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x030C, 0x01CF),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x030F, 0x0208), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0311, 0x020A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0323, 0x1ECA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0328, 0x012E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0049, 0x0330, 0x1E2C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004A, 0x0302, 0x0134),HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x0301, 0x1E30), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x030C, 0x01E8),HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x0323, 0x1E32), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x0327, 0x0136),HB_CODEPOINT_ENCODE3_11_7_14 (0x004B, 0x0331, 0x1E34), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x0301, 0x0139),HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x030C, 0x013D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x0323, 0x1E36),HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x0327, 0x013B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x032D, 0x1E3C),HB_CODEPOINT_ENCODE3_11_7_14 (0x004C, 0x0331, 0x1E3A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004D, 0x0301, 0x1E3E),HB_CODEPOINT_ENCODE3_11_7_14 (0x004D, 0x0307, 0x1E40), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004D, 0x0323, 0x1E42),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0300, 0x01F8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0301, 0x0143),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0303, 0x00D1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0307, 0x1E44),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x030C, 0x0147), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0323, 0x1E46),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0327, 0x0145), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x032D, 0x1E4A),HB_CODEPOINT_ENCODE3_11_7_14 (0x004E, 0x0331, 0x1E48), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0300, 0x00D2),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0301, 0x00D3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0302, 0x00D4),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0303, 0x00D5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0304, 0x014C),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0306, 0x014E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0307, 0x022E),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0308, 0x00D6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0309, 0x1ECE),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x030B, 0x0150), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x030C, 0x01D1),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x030F, 0x020C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0311, 0x020E),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x031B, 0x01A0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0323, 0x1ECC),HB_CODEPOINT_ENCODE3_11_7_14 (0x004F, 0x0328, 0x01EA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050, 0x0301, 0x1E54),HB_CODEPOINT_ENCODE3_11_7_14 (0x0050, 0x0307, 0x1E56), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0301, 0x0154),HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0307, 0x1E58), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x030C, 0x0158),HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x030F, 0x0210), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0311, 0x0212),HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0323, 0x1E5A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0327, 0x0156),HB_CODEPOINT_ENCODE3_11_7_14 (0x0052, 0x0331, 0x1E5E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0301, 0x015A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0302, 0x015C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0307, 0x1E60),HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x030C, 0x0160), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0323, 0x1E62),HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0326, 0x0218), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053, 0x0327, 0x015E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0307, 0x1E6A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x030C, 0x0164),HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0323, 0x1E6C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0326, 0x021A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0327, 0x0162), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x032D, 0x1E70),HB_CODEPOINT_ENCODE3_11_7_14 (0x0054, 0x0331, 0x1E6E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0300, 0x00D9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0301, 0x00DA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0302, 0x00DB),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0303, 0x0168), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0304, 0x016A),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0306, 0x016C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0308, 0x00DC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0309, 0x1EE6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x030A, 0x016E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x030B, 0x0170), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x030C, 0x01D3),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x030F, 0x0214), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0311, 0x0216),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x031B, 0x01AF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0323, 0x1EE4),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0324, 0x1E72), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0328, 0x0172),HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x032D, 0x1E76), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055, 0x0330, 0x1E74),HB_CODEPOINT_ENCODE3_11_7_14 (0x0056, 0x0303, 0x1E7C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056, 0x0323, 0x1E7E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0300, 0x1E80), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0301, 0x1E82),HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0302, 0x0174), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0307, 0x1E86),HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0308, 0x1E84), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057, 0x0323, 0x1E88),HB_CODEPOINT_ENCODE3_11_7_14 (0x0058, 0x0307, 0x1E8A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058, 0x0308, 0x1E8C),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0300, 0x1EF2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0301, 0x00DD),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0302, 0x0176), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0303, 0x1EF8),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0304, 0x0232), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0307, 0x1E8E),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0308, 0x0178), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0309, 0x1EF6),HB_CODEPOINT_ENCODE3_11_7_14 (0x0059, 0x0323, 0x1EF4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0301, 0x0179),HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0302, 0x1E90), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0307, 0x017B),HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x030C, 0x017D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0323, 0x1E92),HB_CODEPOINT_ENCODE3_11_7_14 (0x005A, 0x0331, 0x1E94), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0300, 0x00E0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0301, 0x00E1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0302, 0x00E2),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0303, 0x00E3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0304, 0x0101),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0306, 0x0103), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0307, 0x0227),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0308, 0x00E4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0309, 0x1EA3),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x030A, 0x00E5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x030C, 0x01CE),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x030F, 0x0201), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0311, 0x0203),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0323, 0x1EA1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0325, 0x1E01),HB_CODEPOINT_ENCODE3_11_7_14 (0x0061, 0x0328, 0x0105), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062, 0x0307, 0x1E03),HB_CODEPOINT_ENCODE3_11_7_14 (0x0062, 0x0323, 0x1E05), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062, 0x0331, 0x1E07),HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x0301, 0x0107), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x0302, 0x0109),HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x0307, 0x010B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x030C, 0x010D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0063, 0x0327, 0x00E7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x0307, 0x1E0B),HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x030C, 0x010F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x0323, 0x1E0D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x0327, 0x1E11), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x032D, 0x1E13),HB_CODEPOINT_ENCODE3_11_7_14 (0x0064, 0x0331, 0x1E0F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0300, 0x00E8),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0301, 0x00E9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0302, 0x00EA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0303, 0x1EBD), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0304, 0x0113),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0306, 0x0115), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0307, 0x0117),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0308, 0x00EB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0309, 0x1EBB),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x030C, 0x011B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x030F, 0x0205),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0311, 0x0207), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0323, 0x1EB9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0327, 0x0229), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0328, 0x0119),HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x032D, 0x1E19), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065, 0x0330, 0x1E1B),HB_CODEPOINT_ENCODE3_11_7_14 (0x0066, 0x0307, 0x1E1F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0301, 0x01F5),HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0302, 0x011D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0304, 0x1E21),HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0306, 0x011F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0307, 0x0121),HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x030C, 0x01E7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067, 0x0327, 0x0123),HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0302, 0x0125), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0307, 0x1E23),HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0308, 0x1E27), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x030C, 0x021F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0323, 0x1E25), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0327, 0x1E29),HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x032E, 0x1E2B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068, 0x0331, 0x1E96),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0300, 0x00EC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0301, 0x00ED),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0302, 0x00EE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0303, 0x0129),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0304, 0x012B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0306, 0x012D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0308, 0x00EF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0309, 0x1EC9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x030C, 0x01D0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x030F, 0x0209),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0311, 0x020B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0323, 0x1ECB),HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0328, 0x012F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069, 0x0330, 0x1E2D),HB_CODEPOINT_ENCODE3_11_7_14 (0x006A, 0x0302, 0x0135), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006A, 0x030C, 0x01F0),HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x0301, 0x1E31), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x030C, 0x01E9),HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x0323, 0x1E33), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x0327, 0x0137),HB_CODEPOINT_ENCODE3_11_7_14 (0x006B, 0x0331, 0x1E35), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x0301, 0x013A),HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x030C, 0x013E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x0323, 0x1E37),HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x0327, 0x013C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x032D, 0x1E3D),HB_CODEPOINT_ENCODE3_11_7_14 (0x006C, 0x0331, 0x1E3B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006D, 0x0301, 0x1E3F),HB_CODEPOINT_ENCODE3_11_7_14 (0x006D, 0x0307, 0x1E41), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006D, 0x0323, 0x1E43),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0300, 0x01F9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0301, 0x0144),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0303, 0x00F1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0307, 0x1E45),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x030C, 0x0148), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0323, 0x1E47),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0327, 0x0146), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x032D, 0x1E4B),HB_CODEPOINT_ENCODE3_11_7_14 (0x006E, 0x0331, 0x1E49), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0300, 0x00F2),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0301, 0x00F3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0302, 0x00F4),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0303, 0x00F5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0304, 0x014D),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0306, 0x014F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0307, 0x022F),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0308, 0x00F6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0309, 0x1ECF),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x030B, 0x0151), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x030C, 0x01D2),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x030F, 0x020D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0311, 0x020F),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x031B, 0x01A1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0323, 0x1ECD),HB_CODEPOINT_ENCODE3_11_7_14 (0x006F, 0x0328, 0x01EB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070, 0x0301, 0x1E55),HB_CODEPOINT_ENCODE3_11_7_14 (0x0070, 0x0307, 0x1E57), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0301, 0x0155),HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0307, 0x1E59), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x030C, 0x0159),HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x030F, 0x0211), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0311, 0x0213),HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0323, 0x1E5B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0327, 0x0157),HB_CODEPOINT_ENCODE3_11_7_14 (0x0072, 0x0331, 0x1E5F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0301, 0x015B),HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0302, 0x015D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0307, 0x1E61),HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x030C, 0x0161), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0323, 0x1E63),HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0326, 0x0219), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073, 0x0327, 0x015F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0307, 0x1E6B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0308, 0x1E97),HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x030C, 0x0165), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0323, 0x1E6D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0326, 0x021B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0327, 0x0163),HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x032D, 0x1E71), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074, 0x0331, 0x1E6F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0300, 0x00F9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0301, 0x00FA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0302, 0x00FB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0303, 0x0169),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0304, 0x016B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0306, 0x016D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0308, 0x00FC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0309, 0x1EE7),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x030A, 0x016F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x030B, 0x0171),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x030C, 0x01D4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x030F, 0x0215),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0311, 0x0217), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x031B, 0x01B0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0323, 0x1EE5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0324, 0x1E73),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0328, 0x0173), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x032D, 0x1E77),HB_CODEPOINT_ENCODE3_11_7_14 (0x0075, 0x0330, 0x1E75), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076, 0x0303, 0x1E7D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0076, 0x0323, 0x1E7F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0300, 0x1E81),HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0301, 0x1E83), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0302, 0x0175),HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0307, 0x1E87), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0308, 0x1E85),HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x030A, 0x1E98), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077, 0x0323, 0x1E89),HB_CODEPOINT_ENCODE3_11_7_14 (0x0078, 0x0307, 0x1E8B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078, 0x0308, 0x1E8D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0300, 0x1EF3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0301, 0x00FD),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0302, 0x0177), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0303, 0x1EF9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0304, 0x0233), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0307, 0x1E8F),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0308, 0x00FF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0309, 0x1EF7),HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x030A, 0x1E99), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079, 0x0323, 0x1EF5),HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0301, 0x017A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0302, 0x1E91),HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0307, 0x017C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x030C, 0x017E),HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0323, 0x1E93), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007A, 0x0331, 0x1E95),HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8, 0x0300, 0x1FED), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8, 0x0301, 0x0385),HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8, 0x0342, 0x1FC1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2, 0x0300, 0x1EA6),HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2, 0x0301, 0x1EA4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2, 0x0303, 0x1EAA),HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2, 0x0309, 0x1EA8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C4, 0x0304, 0x01DE),HB_CODEPOINT_ENCODE3_11_7_14 (0x00C5, 0x0301, 0x01FA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6, 0x0301, 0x01FC),HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6, 0x0304, 0x01E2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C7, 0x0301, 0x1E08),HB_CODEPOINT_ENCODE3_11_7_14 (0x00CA, 0x0300, 0x1EC0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CA, 0x0301, 0x1EBE),HB_CODEPOINT_ENCODE3_11_7_14 (0x00CA, 0x0303, 0x1EC4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CA, 0x0309, 0x1EC2),HB_CODEPOINT_ENCODE3_11_7_14 (0x00CF, 0x0301, 0x1E2E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4, 0x0300, 0x1ED2),HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4, 0x0301, 0x1ED0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4, 0x0303, 0x1ED6),HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4, 0x0309, 0x1ED4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5, 0x0301, 0x1E4C),HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5, 0x0304, 0x022C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5, 0x0308, 0x1E4E),HB_CODEPOINT_ENCODE3_11_7_14 (0x00D6, 0x0304, 0x022A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D8, 0x0301, 0x01FE),HB_CODEPOINT_ENCODE3_11_7_14 (0x00DC, 0x0300, 0x01DB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DC, 0x0301, 0x01D7),HB_CODEPOINT_ENCODE3_11_7_14 (0x00DC, 0x0304, 0x01D5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DC, 0x030C, 0x01D9),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2, 0x0300, 0x1EA7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2, 0x0301, 0x1EA5),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2, 0x0303, 0x1EAB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2, 0x0309, 0x1EA9),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E4, 0x0304, 0x01DF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E5, 0x0301, 0x01FB),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6, 0x0301, 0x01FD), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6, 0x0304, 0x01E3),HB_CODEPOINT_ENCODE3_11_7_14 (0x00E7, 0x0301, 0x1E09), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EA, 0x0300, 0x1EC1),HB_CODEPOINT_ENCODE3_11_7_14 (0x00EA, 0x0301, 0x1EBF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EA, 0x0303, 0x1EC5),HB_CODEPOINT_ENCODE3_11_7_14 (0x00EA, 0x0309, 0x1EC3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EF, 0x0301, 0x1E2F),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4, 0x0300, 0x1ED3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4, 0x0301, 0x1ED1),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4, 0x0303, 0x1ED7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4, 0x0309, 0x1ED5),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5, 0x0301, 0x1E4D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5, 0x0304, 0x022D),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5, 0x0308, 0x1E4F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F6, 0x0304, 0x022B),HB_CODEPOINT_ENCODE3_11_7_14 (0x00F8, 0x0301, 0x01FF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FC, 0x0300, 0x01DC),HB_CODEPOINT_ENCODE3_11_7_14 (0x00FC, 0x0301, 0x01D8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FC, 0x0304, 0x01D6),HB_CODEPOINT_ENCODE3_11_7_14 (0x00FC, 0x030C, 0x01DA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102, 0x0300, 0x1EB0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0102, 0x0301, 0x1EAE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102, 0x0303, 0x1EB4),HB_CODEPOINT_ENCODE3_11_7_14 (0x0102, 0x0309, 0x1EB2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103, 0x0300, 0x1EB1),HB_CODEPOINT_ENCODE3_11_7_14 (0x0103, 0x0301, 0x1EAF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103, 0x0303, 0x1EB5),HB_CODEPOINT_ENCODE3_11_7_14 (0x0103, 0x0309, 0x1EB3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112, 0x0300, 0x1E14),HB_CODEPOINT_ENCODE3_11_7_14 (0x0112, 0x0301, 0x1E16), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113, 0x0300, 0x1E15),HB_CODEPOINT_ENCODE3_11_7_14 (0x0113, 0x0301, 0x1E17), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014C, 0x0300, 0x1E50),HB_CODEPOINT_ENCODE3_11_7_14 (0x014C, 0x0301, 0x1E52), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014D, 0x0300, 0x1E51),HB_CODEPOINT_ENCODE3_11_7_14 (0x014D, 0x0301, 0x1E53), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015A, 0x0307, 0x1E64),HB_CODEPOINT_ENCODE3_11_7_14 (0x015B, 0x0307, 0x1E65), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0160, 0x0307, 0x1E66),HB_CODEPOINT_ENCODE3_11_7_14 (0x0161, 0x0307, 0x1E67), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0168, 0x0301, 0x1E78),HB_CODEPOINT_ENCODE3_11_7_14 (0x0169, 0x0301, 0x1E79), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016A, 0x0308, 0x1E7A),HB_CODEPOINT_ENCODE3_11_7_14 (0x016B, 0x0308, 0x1E7B), + HB_CODEPOINT_ENCODE3_11_7_14 (0x017F, 0x0307, 0x1E9B),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0300, 0x1EDC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0301, 0x1EDA),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0303, 0x1EE0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0309, 0x1EDE),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0, 0x0323, 0x1EE2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0300, 0x1EDD),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0301, 0x1EDB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0303, 0x1EE1),HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0309, 0x1EDF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1, 0x0323, 0x1EE3),HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0300, 0x1EEA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0301, 0x1EE8),HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0303, 0x1EEE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0309, 0x1EEC),HB_CODEPOINT_ENCODE3_11_7_14 (0x01AF, 0x0323, 0x1EF0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0300, 0x1EEB),HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0301, 0x1EE9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0303, 0x1EEF),HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0309, 0x1EED), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0, 0x0323, 0x1EF1),HB_CODEPOINT_ENCODE3_11_7_14 (0x01B7, 0x030C, 0x01EE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EA, 0x0304, 0x01EC),HB_CODEPOINT_ENCODE3_11_7_14 (0x01EB, 0x0304, 0x01ED), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0226, 0x0304, 0x01E0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0227, 0x0304, 0x01E1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0228, 0x0306, 0x1E1C),HB_CODEPOINT_ENCODE3_11_7_14 (0x0229, 0x0306, 0x1E1D), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022E, 0x0304, 0x0230),HB_CODEPOINT_ENCODE3_11_7_14 (0x022F, 0x0304, 0x0231), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0292, 0x030C, 0x01EF),HB_CODEPOINT_ENCODE3_11_7_14 (0x0308, 0x0301, 0x0000), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0300, 0x1FBA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0301, 0x0386), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0304, 0x1FB9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0306, 0x1FB8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0313, 0x1F08),HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0314, 0x1F09), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391, 0x0345, 0x1FBC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0395, 0x0300, 0x1FC8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395, 0x0301, 0x0388),HB_CODEPOINT_ENCODE3_11_7_14 (0x0395, 0x0313, 0x1F18), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395, 0x0314, 0x1F19),HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0300, 0x1FCA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0301, 0x0389),HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0313, 0x1F28), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0314, 0x1F29),HB_CODEPOINT_ENCODE3_11_7_14 (0x0397, 0x0345, 0x1FCC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0300, 0x1FDA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0301, 0x038A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0304, 0x1FD9),HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0306, 0x1FD8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0308, 0x03AA),HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0313, 0x1F38), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399, 0x0314, 0x1F39),HB_CODEPOINT_ENCODE3_11_7_14 (0x039F, 0x0300, 0x1FF8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039F, 0x0301, 0x038C),HB_CODEPOINT_ENCODE3_11_7_14 (0x039F, 0x0313, 0x1F48), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039F, 0x0314, 0x1F49),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A1, 0x0314, 0x1FEC), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0300, 0x1FEA),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0301, 0x038E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0304, 0x1FE9),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0306, 0x1FE8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0308, 0x03AB),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5, 0x0314, 0x1F59), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0300, 0x1FFA),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0301, 0x038F), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0313, 0x1F68),HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0314, 0x1F69), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9, 0x0345, 0x1FFC),HB_CODEPOINT_ENCODE3_11_7_14 (0x03AC, 0x0345, 0x1FB4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03AE, 0x0345, 0x1FC4),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0300, 0x1F70), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0301, 0x03AC),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0304, 0x1FB1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0306, 0x1FB0),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0313, 0x1F00), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0314, 0x1F01),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0342, 0x1FB6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1, 0x0345, 0x1FB3),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5, 0x0300, 0x1F72), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5, 0x0301, 0x03AD),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5, 0x0313, 0x1F10), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5, 0x0314, 0x1F11),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0300, 0x1F74), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0301, 0x03AE),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0313, 0x1F20), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0314, 0x1F21),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0342, 0x1FC6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7, 0x0345, 0x1FC3),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0300, 0x1F76), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0301, 0x03AF),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0304, 0x1FD1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0306, 0x1FD0),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0308, 0x03CA), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0313, 0x1F30),HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0314, 0x1F31), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9, 0x0342, 0x1FD6),HB_CODEPOINT_ENCODE3_11_7_14 (0x03BF, 0x0300, 0x1F78), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BF, 0x0301, 0x03CC),HB_CODEPOINT_ENCODE3_11_7_14 (0x03BF, 0x0313, 0x1F40), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BF, 0x0314, 0x1F41),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1, 0x0313, 0x1FE4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1, 0x0314, 0x1FE5),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0300, 0x1F7A), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0301, 0x03CD),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0304, 0x1FE1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0306, 0x1FE0),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0308, 0x03CB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0313, 0x1F50),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0314, 0x1F51), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5, 0x0342, 0x1FE6),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0300, 0x1F7C), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0301, 0x03CE),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0313, 0x1F60), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0314, 0x1F61),HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0342, 0x1FF6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9, 0x0345, 0x1FF3),HB_CODEPOINT_ENCODE3_11_7_14 (0x03CA, 0x0300, 0x1FD2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CA, 0x0301, 0x0390),HB_CODEPOINT_ENCODE3_11_7_14 (0x03CA, 0x0342, 0x1FD7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CB, 0x0300, 0x1FE2),HB_CODEPOINT_ENCODE3_11_7_14 (0x03CB, 0x0301, 0x03B0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CB, 0x0342, 0x1FE7),HB_CODEPOINT_ENCODE3_11_7_14 (0x03CE, 0x0345, 0x1FF4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2, 0x0301, 0x03D3),HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2, 0x0308, 0x03D4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0406, 0x0308, 0x0407),HB_CODEPOINT_ENCODE3_11_7_14 (0x0410, 0x0306, 0x04D0), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410, 0x0308, 0x04D2),HB_CODEPOINT_ENCODE3_11_7_14 (0x0413, 0x0301, 0x0403), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415, 0x0300, 0x0400),HB_CODEPOINT_ENCODE3_11_7_14 (0x0415, 0x0306, 0x04D6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415, 0x0308, 0x0401),HB_CODEPOINT_ENCODE3_11_7_14 (0x0416, 0x0306, 0x04C1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416, 0x0308, 0x04DC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0417, 0x0308, 0x04DE), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418, 0x0300, 0x040D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0418, 0x0304, 0x04E2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418, 0x0306, 0x0419),HB_CODEPOINT_ENCODE3_11_7_14 (0x0418, 0x0308, 0x04E4), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041A, 0x0301, 0x040C),HB_CODEPOINT_ENCODE3_11_7_14 (0x041E, 0x0308, 0x04E6), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423, 0x0304, 0x04EE),HB_CODEPOINT_ENCODE3_11_7_14 (0x0423, 0x0306, 0x040E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423, 0x0308, 0x04F0),HB_CODEPOINT_ENCODE3_11_7_14 (0x0423, 0x030B, 0x04F2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0427, 0x0308, 0x04F4),HB_CODEPOINT_ENCODE3_11_7_14 (0x042B, 0x0308, 0x04F8), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042D, 0x0308, 0x04EC),HB_CODEPOINT_ENCODE3_11_7_14 (0x0430, 0x0306, 0x04D1), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430, 0x0308, 0x04D3),HB_CODEPOINT_ENCODE3_11_7_14 (0x0433, 0x0301, 0x0453), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435, 0x0300, 0x0450),HB_CODEPOINT_ENCODE3_11_7_14 (0x0435, 0x0306, 0x04D7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435, 0x0308, 0x0451),HB_CODEPOINT_ENCODE3_11_7_14 (0x0436, 0x0306, 0x04C2), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436, 0x0308, 0x04DD),HB_CODEPOINT_ENCODE3_11_7_14 (0x0437, 0x0308, 0x04DF), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438, 0x0300, 0x045D),HB_CODEPOINT_ENCODE3_11_7_14 (0x0438, 0x0304, 0x04E3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438, 0x0306, 0x0439),HB_CODEPOINT_ENCODE3_11_7_14 (0x0438, 0x0308, 0x04E5), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043A, 0x0301, 0x045C),HB_CODEPOINT_ENCODE3_11_7_14 (0x043E, 0x0308, 0x04E7), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443, 0x0304, 0x04EF),HB_CODEPOINT_ENCODE3_11_7_14 (0x0443, 0x0306, 0x045E), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443, 0x0308, 0x04F1),HB_CODEPOINT_ENCODE3_11_7_14 (0x0443, 0x030B, 0x04F3), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0447, 0x0308, 0x04F5),HB_CODEPOINT_ENCODE3_11_7_14 (0x044B, 0x0308, 0x04F9), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044D, 0x0308, 0x04ED),HB_CODEPOINT_ENCODE3_11_7_14 (0x0456, 0x0308, 0x0457), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0474, 0x030F, 0x0476),HB_CODEPOINT_ENCODE3_11_7_14 (0x0475, 0x030F, 0x0477), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D8, 0x0308, 0x04DA),HB_CODEPOINT_ENCODE3_11_7_14 (0x04D9, 0x0308, 0x04DB), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E8, 0x0308, 0x04EA),HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9, 0x0308, 0x04EB), }; -static const uint64_t -_hb_ucd_dm2_u64_map[408] = +static const uint64_t _hb_ucd_dm2_u64_map[408]= { - HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B8u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D2u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D3u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D4u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05B9u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D8u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05B4u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05DAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DCu, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05DEu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E0u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E1u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E3u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BFu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E7u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E8u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C2u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x05EAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05F2u, 0x05B7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0627u, 0x0653u, 0x0622u), HB_CODEPOINT_ENCODE3 (0x0627u, 0x0654u, 0x0623u), - HB_CODEPOINT_ENCODE3 (0x0627u, 0x0655u, 0x0625u), HB_CODEPOINT_ENCODE3 (0x0648u, 0x0654u, 0x0624u), - HB_CODEPOINT_ENCODE3 (0x064Au, 0x0654u, 0x0626u), HB_CODEPOINT_ENCODE3 (0x06C1u, 0x0654u, 0x06C2u), - HB_CODEPOINT_ENCODE3 (0x06D2u, 0x0654u, 0x06D3u), HB_CODEPOINT_ENCODE3 (0x06D5u, 0x0654u, 0x06C0u), - HB_CODEPOINT_ENCODE3 (0x0915u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0916u, 0x093Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0917u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x091Cu, 0x093Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0921u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0922u, 0x093Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0928u, 0x093Cu, 0x0929u), HB_CODEPOINT_ENCODE3 (0x092Bu, 0x093Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x092Fu, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0930u, 0x093Cu, 0x0931u), - HB_CODEPOINT_ENCODE3 (0x0933u, 0x093Cu, 0x0934u), HB_CODEPOINT_ENCODE3 (0x09A1u, 0x09BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x09A2u, 0x09BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x09AFu, 0x09BCu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09BEu, 0x09CBu), HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09D7u, 0x09CCu), - HB_CODEPOINT_ENCODE3 (0x0A16u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A17u, 0x0A3Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0A1Cu, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A2Bu, 0x0A3Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0A32u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A38u, 0x0A3Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0B21u, 0x0B3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0B22u, 0x0B3Cu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B3Eu, 0x0B4Bu), HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B56u, 0x0B48u), - HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B57u, 0x0B4Cu), HB_CODEPOINT_ENCODE3 (0x0B92u, 0x0BD7u, 0x0B94u), - HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BBEu, 0x0BCAu), HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BD7u, 0x0BCCu), - HB_CODEPOINT_ENCODE3 (0x0BC7u, 0x0BBEu, 0x0BCBu), HB_CODEPOINT_ENCODE3 (0x0C46u, 0x0C56u, 0x0C48u), - HB_CODEPOINT_ENCODE3 (0x0CBFu, 0x0CD5u, 0x0CC0u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CC2u, 0x0CCAu), - HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD5u, 0x0CC7u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD6u, 0x0CC8u), - HB_CODEPOINT_ENCODE3 (0x0CCAu, 0x0CD5u, 0x0CCBu), HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D3Eu, 0x0D4Au), - HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D57u, 0x0D4Cu), HB_CODEPOINT_ENCODE3 (0x0D47u, 0x0D3Eu, 0x0D4Bu), - HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCAu, 0x0DDAu), HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCFu, 0x0DDCu), - HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DDFu, 0x0DDEu), HB_CODEPOINT_ENCODE3 (0x0DDCu, 0x0DCAu, 0x0DDDu), - HB_CODEPOINT_ENCODE3 (0x0F40u, 0x0FB5u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F42u, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F4Cu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F51u, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F56u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F5Bu, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F72u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F74u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F90u, 0x0FB5u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0F92u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F9Cu, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0FA1u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FA6u, 0x0FB7u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0FABu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FB2u, 0x0F80u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x0FB3u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1025u, 0x102Eu, 0x1026u), - HB_CODEPOINT_ENCODE3 (0x1B05u, 0x1B35u, 0x1B06u), HB_CODEPOINT_ENCODE3 (0x1B07u, 0x1B35u, 0x1B08u), - HB_CODEPOINT_ENCODE3 (0x1B09u, 0x1B35u, 0x1B0Au), HB_CODEPOINT_ENCODE3 (0x1B0Bu, 0x1B35u, 0x1B0Cu), - HB_CODEPOINT_ENCODE3 (0x1B0Du, 0x1B35u, 0x1B0Eu), HB_CODEPOINT_ENCODE3 (0x1B11u, 0x1B35u, 0x1B12u), - HB_CODEPOINT_ENCODE3 (0x1B3Au, 0x1B35u, 0x1B3Bu), HB_CODEPOINT_ENCODE3 (0x1B3Cu, 0x1B35u, 0x1B3Du), - HB_CODEPOINT_ENCODE3 (0x1B3Eu, 0x1B35u, 0x1B40u), HB_CODEPOINT_ENCODE3 (0x1B3Fu, 0x1B35u, 0x1B41u), - HB_CODEPOINT_ENCODE3 (0x1B42u, 0x1B35u, 0x1B43u), HB_CODEPOINT_ENCODE3 (0x1E36u, 0x0304u, 0x1E38u), - HB_CODEPOINT_ENCODE3 (0x1E37u, 0x0304u, 0x1E39u), HB_CODEPOINT_ENCODE3 (0x1E5Au, 0x0304u, 0x1E5Cu), - HB_CODEPOINT_ENCODE3 (0x1E5Bu, 0x0304u, 0x1E5Du), HB_CODEPOINT_ENCODE3 (0x1E62u, 0x0307u, 0x1E68u), - HB_CODEPOINT_ENCODE3 (0x1E63u, 0x0307u, 0x1E69u), HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0302u, 0x1EACu), - HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0306u, 0x1EB6u), HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0302u, 0x1EADu), - HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0306u, 0x1EB7u), HB_CODEPOINT_ENCODE3 (0x1EB8u, 0x0302u, 0x1EC6u), - HB_CODEPOINT_ENCODE3 (0x1EB9u, 0x0302u, 0x1EC7u), HB_CODEPOINT_ENCODE3 (0x1ECCu, 0x0302u, 0x1ED8u), - HB_CODEPOINT_ENCODE3 (0x1ECDu, 0x0302u, 0x1ED9u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0300u, 0x1F02u), - HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0301u, 0x1F04u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0342u, 0x1F06u), - HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0345u, 0x1F80u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0300u, 0x1F03u), - HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0301u, 0x1F05u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0342u, 0x1F07u), - HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0345u, 0x1F81u), HB_CODEPOINT_ENCODE3 (0x1F02u, 0x0345u, 0x1F82u), - HB_CODEPOINT_ENCODE3 (0x1F03u, 0x0345u, 0x1F83u), HB_CODEPOINT_ENCODE3 (0x1F04u, 0x0345u, 0x1F84u), - HB_CODEPOINT_ENCODE3 (0x1F05u, 0x0345u, 0x1F85u), HB_CODEPOINT_ENCODE3 (0x1F06u, 0x0345u, 0x1F86u), - HB_CODEPOINT_ENCODE3 (0x1F07u, 0x0345u, 0x1F87u), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0300u, 0x1F0Au), - HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0301u, 0x1F0Cu), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0342u, 0x1F0Eu), - HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0345u, 0x1F88u), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0300u, 0x1F0Bu), - HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0301u, 0x1F0Du), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0342u, 0x1F0Fu), - HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0345u, 0x1F89u), HB_CODEPOINT_ENCODE3 (0x1F0Au, 0x0345u, 0x1F8Au), - HB_CODEPOINT_ENCODE3 (0x1F0Bu, 0x0345u, 0x1F8Bu), HB_CODEPOINT_ENCODE3 (0x1F0Cu, 0x0345u, 0x1F8Cu), - HB_CODEPOINT_ENCODE3 (0x1F0Du, 0x0345u, 0x1F8Du), HB_CODEPOINT_ENCODE3 (0x1F0Eu, 0x0345u, 0x1F8Eu), - HB_CODEPOINT_ENCODE3 (0x1F0Fu, 0x0345u, 0x1F8Fu), HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0300u, 0x1F12u), - HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0301u, 0x1F14u), HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0300u, 0x1F13u), - HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0301u, 0x1F15u), HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0300u, 0x1F1Au), - HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0301u, 0x1F1Cu), HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0300u, 0x1F1Bu), - HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0301u, 0x1F1Du), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0300u, 0x1F22u), - HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0301u, 0x1F24u), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0342u, 0x1F26u), - HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0345u, 0x1F90u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0300u, 0x1F23u), - HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0301u, 0x1F25u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0342u, 0x1F27u), - HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0345u, 0x1F91u), HB_CODEPOINT_ENCODE3 (0x1F22u, 0x0345u, 0x1F92u), - HB_CODEPOINT_ENCODE3 (0x1F23u, 0x0345u, 0x1F93u), HB_CODEPOINT_ENCODE3 (0x1F24u, 0x0345u, 0x1F94u), - HB_CODEPOINT_ENCODE3 (0x1F25u, 0x0345u, 0x1F95u), HB_CODEPOINT_ENCODE3 (0x1F26u, 0x0345u, 0x1F96u), - HB_CODEPOINT_ENCODE3 (0x1F27u, 0x0345u, 0x1F97u), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0300u, 0x1F2Au), - HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0301u, 0x1F2Cu), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0342u, 0x1F2Eu), - HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0345u, 0x1F98u), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0300u, 0x1F2Bu), - HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0301u, 0x1F2Du), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0342u, 0x1F2Fu), - HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0345u, 0x1F99u), HB_CODEPOINT_ENCODE3 (0x1F2Au, 0x0345u, 0x1F9Au), - HB_CODEPOINT_ENCODE3 (0x1F2Bu, 0x0345u, 0x1F9Bu), HB_CODEPOINT_ENCODE3 (0x1F2Cu, 0x0345u, 0x1F9Cu), - HB_CODEPOINT_ENCODE3 (0x1F2Du, 0x0345u, 0x1F9Du), HB_CODEPOINT_ENCODE3 (0x1F2Eu, 0x0345u, 0x1F9Eu), - HB_CODEPOINT_ENCODE3 (0x1F2Fu, 0x0345u, 0x1F9Fu), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0300u, 0x1F32u), - HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0301u, 0x1F34u), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0342u, 0x1F36u), - HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0300u, 0x1F33u), HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0301u, 0x1F35u), - HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0342u, 0x1F37u), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0300u, 0x1F3Au), - HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0301u, 0x1F3Cu), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0342u, 0x1F3Eu), - HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0300u, 0x1F3Bu), HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0301u, 0x1F3Du), - HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0342u, 0x1F3Fu), HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0300u, 0x1F42u), - HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0301u, 0x1F44u), HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0300u, 0x1F43u), - HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0301u, 0x1F45u), HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0300u, 0x1F4Au), - HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0301u, 0x1F4Cu), HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0300u, 0x1F4Bu), - HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0301u, 0x1F4Du), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0300u, 0x1F52u), - HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0301u, 0x1F54u), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0342u, 0x1F56u), - HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0300u, 0x1F53u), HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0301u, 0x1F55u), - HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0342u, 0x1F57u), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0300u, 0x1F5Bu), - HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0301u, 0x1F5Du), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0342u, 0x1F5Fu), - HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0300u, 0x1F62u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0301u, 0x1F64u), - HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0342u, 0x1F66u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0345u, 0x1FA0u), - HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0300u, 0x1F63u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0301u, 0x1F65u), - HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0342u, 0x1F67u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0345u, 0x1FA1u), - HB_CODEPOINT_ENCODE3 (0x1F62u, 0x0345u, 0x1FA2u), HB_CODEPOINT_ENCODE3 (0x1F63u, 0x0345u, 0x1FA3u), - HB_CODEPOINT_ENCODE3 (0x1F64u, 0x0345u, 0x1FA4u), HB_CODEPOINT_ENCODE3 (0x1F65u, 0x0345u, 0x1FA5u), - HB_CODEPOINT_ENCODE3 (0x1F66u, 0x0345u, 0x1FA6u), HB_CODEPOINT_ENCODE3 (0x1F67u, 0x0345u, 0x1FA7u), - HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0300u, 0x1F6Au), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0301u, 0x1F6Cu), - HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0342u, 0x1F6Eu), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0345u, 0x1FA8u), - HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0300u, 0x1F6Bu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0301u, 0x1F6Du), - HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0342u, 0x1F6Fu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0345u, 0x1FA9u), - HB_CODEPOINT_ENCODE3 (0x1F6Au, 0x0345u, 0x1FAAu), HB_CODEPOINT_ENCODE3 (0x1F6Bu, 0x0345u, 0x1FABu), - HB_CODEPOINT_ENCODE3 (0x1F6Cu, 0x0345u, 0x1FACu), HB_CODEPOINT_ENCODE3 (0x1F6Du, 0x0345u, 0x1FADu), - HB_CODEPOINT_ENCODE3 (0x1F6Eu, 0x0345u, 0x1FAEu), HB_CODEPOINT_ENCODE3 (0x1F6Fu, 0x0345u, 0x1FAFu), - HB_CODEPOINT_ENCODE3 (0x1F70u, 0x0345u, 0x1FB2u), HB_CODEPOINT_ENCODE3 (0x1F74u, 0x0345u, 0x1FC2u), - HB_CODEPOINT_ENCODE3 (0x1F7Cu, 0x0345u, 0x1FF2u), HB_CODEPOINT_ENCODE3 (0x1FB6u, 0x0345u, 0x1FB7u), - HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0300u, 0x1FCDu), HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0301u, 0x1FCEu), - HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0342u, 0x1FCFu), HB_CODEPOINT_ENCODE3 (0x1FC6u, 0x0345u, 0x1FC7u), - HB_CODEPOINT_ENCODE3 (0x1FF6u, 0x0345u, 0x1FF7u), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0300u, 0x1FDDu), - HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0301u, 0x1FDEu), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0342u, 0x1FDFu), - HB_CODEPOINT_ENCODE3 (0x2190u, 0x0338u, 0x219Au), HB_CODEPOINT_ENCODE3 (0x2192u, 0x0338u, 0x219Bu), - HB_CODEPOINT_ENCODE3 (0x2194u, 0x0338u, 0x21AEu), HB_CODEPOINT_ENCODE3 (0x21D0u, 0x0338u, 0x21CDu), - HB_CODEPOINT_ENCODE3 (0x21D2u, 0x0338u, 0x21CFu), HB_CODEPOINT_ENCODE3 (0x21D4u, 0x0338u, 0x21CEu), - HB_CODEPOINT_ENCODE3 (0x2203u, 0x0338u, 0x2204u), HB_CODEPOINT_ENCODE3 (0x2208u, 0x0338u, 0x2209u), - HB_CODEPOINT_ENCODE3 (0x220Bu, 0x0338u, 0x220Cu), HB_CODEPOINT_ENCODE3 (0x2223u, 0x0338u, 0x2224u), - HB_CODEPOINT_ENCODE3 (0x2225u, 0x0338u, 0x2226u), HB_CODEPOINT_ENCODE3 (0x223Cu, 0x0338u, 0x2241u), - HB_CODEPOINT_ENCODE3 (0x2243u, 0x0338u, 0x2244u), HB_CODEPOINT_ENCODE3 (0x2245u, 0x0338u, 0x2247u), - HB_CODEPOINT_ENCODE3 (0x2248u, 0x0338u, 0x2249u), HB_CODEPOINT_ENCODE3 (0x224Du, 0x0338u, 0x226Du), - HB_CODEPOINT_ENCODE3 (0x2261u, 0x0338u, 0x2262u), HB_CODEPOINT_ENCODE3 (0x2264u, 0x0338u, 0x2270u), - HB_CODEPOINT_ENCODE3 (0x2265u, 0x0338u, 0x2271u), HB_CODEPOINT_ENCODE3 (0x2272u, 0x0338u, 0x2274u), - HB_CODEPOINT_ENCODE3 (0x2273u, 0x0338u, 0x2275u), HB_CODEPOINT_ENCODE3 (0x2276u, 0x0338u, 0x2278u), - HB_CODEPOINT_ENCODE3 (0x2277u, 0x0338u, 0x2279u), HB_CODEPOINT_ENCODE3 (0x227Au, 0x0338u, 0x2280u), - HB_CODEPOINT_ENCODE3 (0x227Bu, 0x0338u, 0x2281u), HB_CODEPOINT_ENCODE3 (0x227Cu, 0x0338u, 0x22E0u), - HB_CODEPOINT_ENCODE3 (0x227Du, 0x0338u, 0x22E1u), HB_CODEPOINT_ENCODE3 (0x2282u, 0x0338u, 0x2284u), - HB_CODEPOINT_ENCODE3 (0x2283u, 0x0338u, 0x2285u), HB_CODEPOINT_ENCODE3 (0x2286u, 0x0338u, 0x2288u), - HB_CODEPOINT_ENCODE3 (0x2287u, 0x0338u, 0x2289u), HB_CODEPOINT_ENCODE3 (0x2291u, 0x0338u, 0x22E2u), - HB_CODEPOINT_ENCODE3 (0x2292u, 0x0338u, 0x22E3u), HB_CODEPOINT_ENCODE3 (0x22A2u, 0x0338u, 0x22ACu), - HB_CODEPOINT_ENCODE3 (0x22A8u, 0x0338u, 0x22ADu), HB_CODEPOINT_ENCODE3 (0x22A9u, 0x0338u, 0x22AEu), - HB_CODEPOINT_ENCODE3 (0x22ABu, 0x0338u, 0x22AFu), HB_CODEPOINT_ENCODE3 (0x22B2u, 0x0338u, 0x22EAu), - HB_CODEPOINT_ENCODE3 (0x22B3u, 0x0338u, 0x22EBu), HB_CODEPOINT_ENCODE3 (0x22B4u, 0x0338u, 0x22ECu), - HB_CODEPOINT_ENCODE3 (0x22B5u, 0x0338u, 0x22EDu), HB_CODEPOINT_ENCODE3 (0x2ADDu, 0x0338u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x3046u, 0x3099u, 0x3094u), HB_CODEPOINT_ENCODE3 (0x304Bu, 0x3099u, 0x304Cu), - HB_CODEPOINT_ENCODE3 (0x304Du, 0x3099u, 0x304Eu), HB_CODEPOINT_ENCODE3 (0x304Fu, 0x3099u, 0x3050u), - HB_CODEPOINT_ENCODE3 (0x3051u, 0x3099u, 0x3052u), HB_CODEPOINT_ENCODE3 (0x3053u, 0x3099u, 0x3054u), - HB_CODEPOINT_ENCODE3 (0x3055u, 0x3099u, 0x3056u), HB_CODEPOINT_ENCODE3 (0x3057u, 0x3099u, 0x3058u), - HB_CODEPOINT_ENCODE3 (0x3059u, 0x3099u, 0x305Au), HB_CODEPOINT_ENCODE3 (0x305Bu, 0x3099u, 0x305Cu), - HB_CODEPOINT_ENCODE3 (0x305Du, 0x3099u, 0x305Eu), HB_CODEPOINT_ENCODE3 (0x305Fu, 0x3099u, 0x3060u), - HB_CODEPOINT_ENCODE3 (0x3061u, 0x3099u, 0x3062u), HB_CODEPOINT_ENCODE3 (0x3064u, 0x3099u, 0x3065u), - HB_CODEPOINT_ENCODE3 (0x3066u, 0x3099u, 0x3067u), HB_CODEPOINT_ENCODE3 (0x3068u, 0x3099u, 0x3069u), - HB_CODEPOINT_ENCODE3 (0x306Fu, 0x3099u, 0x3070u), HB_CODEPOINT_ENCODE3 (0x306Fu, 0x309Au, 0x3071u), - HB_CODEPOINT_ENCODE3 (0x3072u, 0x3099u, 0x3073u), HB_CODEPOINT_ENCODE3 (0x3072u, 0x309Au, 0x3074u), - HB_CODEPOINT_ENCODE3 (0x3075u, 0x3099u, 0x3076u), HB_CODEPOINT_ENCODE3 (0x3075u, 0x309Au, 0x3077u), - HB_CODEPOINT_ENCODE3 (0x3078u, 0x3099u, 0x3079u), HB_CODEPOINT_ENCODE3 (0x3078u, 0x309Au, 0x307Au), - HB_CODEPOINT_ENCODE3 (0x307Bu, 0x3099u, 0x307Cu), HB_CODEPOINT_ENCODE3 (0x307Bu, 0x309Au, 0x307Du), - HB_CODEPOINT_ENCODE3 (0x309Du, 0x3099u, 0x309Eu), HB_CODEPOINT_ENCODE3 (0x30A6u, 0x3099u, 0x30F4u), - HB_CODEPOINT_ENCODE3 (0x30ABu, 0x3099u, 0x30ACu), HB_CODEPOINT_ENCODE3 (0x30ADu, 0x3099u, 0x30AEu), - HB_CODEPOINT_ENCODE3 (0x30AFu, 0x3099u, 0x30B0u), HB_CODEPOINT_ENCODE3 (0x30B1u, 0x3099u, 0x30B2u), - HB_CODEPOINT_ENCODE3 (0x30B3u, 0x3099u, 0x30B4u), HB_CODEPOINT_ENCODE3 (0x30B5u, 0x3099u, 0x30B6u), - HB_CODEPOINT_ENCODE3 (0x30B7u, 0x3099u, 0x30B8u), HB_CODEPOINT_ENCODE3 (0x30B9u, 0x3099u, 0x30BAu), - HB_CODEPOINT_ENCODE3 (0x30BBu, 0x3099u, 0x30BCu), HB_CODEPOINT_ENCODE3 (0x30BDu, 0x3099u, 0x30BEu), - HB_CODEPOINT_ENCODE3 (0x30BFu, 0x3099u, 0x30C0u), HB_CODEPOINT_ENCODE3 (0x30C1u, 0x3099u, 0x30C2u), - HB_CODEPOINT_ENCODE3 (0x30C4u, 0x3099u, 0x30C5u), HB_CODEPOINT_ENCODE3 (0x30C6u, 0x3099u, 0x30C7u), - HB_CODEPOINT_ENCODE3 (0x30C8u, 0x3099u, 0x30C9u), HB_CODEPOINT_ENCODE3 (0x30CFu, 0x3099u, 0x30D0u), - HB_CODEPOINT_ENCODE3 (0x30CFu, 0x309Au, 0x30D1u), HB_CODEPOINT_ENCODE3 (0x30D2u, 0x3099u, 0x30D3u), - HB_CODEPOINT_ENCODE3 (0x30D2u, 0x309Au, 0x30D4u), HB_CODEPOINT_ENCODE3 (0x30D5u, 0x3099u, 0x30D6u), - HB_CODEPOINT_ENCODE3 (0x30D5u, 0x309Au, 0x30D7u), HB_CODEPOINT_ENCODE3 (0x30D8u, 0x3099u, 0x30D9u), - HB_CODEPOINT_ENCODE3 (0x30D8u, 0x309Au, 0x30DAu), HB_CODEPOINT_ENCODE3 (0x30DBu, 0x3099u, 0x30DCu), - HB_CODEPOINT_ENCODE3 (0x30DBu, 0x309Au, 0x30DDu), HB_CODEPOINT_ENCODE3 (0x30EFu, 0x3099u, 0x30F7u), - HB_CODEPOINT_ENCODE3 (0x30F0u, 0x3099u, 0x30F8u), HB_CODEPOINT_ENCODE3 (0x30F1u, 0x3099u, 0x30F9u), - HB_CODEPOINT_ENCODE3 (0x30F2u, 0x3099u, 0x30FAu), HB_CODEPOINT_ENCODE3 (0x30FDu, 0x3099u, 0x30FEu), - HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C2u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x105D2u, 0x0307u, 0x105C9u), HB_CODEPOINT_ENCODE3 (0x105DAu, 0x0307u, 0x105E4u), - HB_CODEPOINT_ENCODE3 (0x11099u, 0x110BAu, 0x1109Au),HB_CODEPOINT_ENCODE3 (0x1109Bu, 0x110BAu, 0x1109Cu), - HB_CODEPOINT_ENCODE3 (0x110A5u, 0x110BAu, 0x110ABu),HB_CODEPOINT_ENCODE3 (0x11131u, 0x11127u, 0x1112Eu), - HB_CODEPOINT_ENCODE3 (0x11132u, 0x11127u, 0x1112Fu),HB_CODEPOINT_ENCODE3 (0x11347u, 0x1133Eu, 0x1134Bu), - HB_CODEPOINT_ENCODE3 (0x11347u, 0x11357u, 0x1134Cu),HB_CODEPOINT_ENCODE3 (0x11382u, 0x113C9u, 0x11383u), - HB_CODEPOINT_ENCODE3 (0x11384u, 0x113BBu, 0x11385u),HB_CODEPOINT_ENCODE3 (0x1138Bu, 0x113C2u, 0x1138Eu), - HB_CODEPOINT_ENCODE3 (0x11390u, 0x113C9u, 0x11391u),HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113B8u, 0x113C7u), - HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113C2u, 0x113C5u),HB_CODEPOINT_ENCODE3 (0x113C2u, 0x113C9u, 0x113C8u), - HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114B0u, 0x114BCu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BAu, 0x114BBu), - HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BDu, 0x114BEu),HB_CODEPOINT_ENCODE3 (0x115B8u, 0x115AFu, 0x115BAu), - HB_CODEPOINT_ENCODE3 (0x115B9u, 0x115AFu, 0x115BBu),HB_CODEPOINT_ENCODE3 (0x11935u, 0x11930u, 0x11938u), - HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x1611Eu, 0x16121u),HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x1611Fu, 0x16123u), - HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x16120u, 0x16125u),HB_CODEPOINT_ENCODE3 (0x1611Eu, 0x16129u, 0x16122u), - HB_CODEPOINT_ENCODE3 (0x16121u, 0x1611Fu, 0x16126u),HB_CODEPOINT_ENCODE3 (0x16121u, 0x16120u, 0x16128u), - HB_CODEPOINT_ENCODE3 (0x16122u, 0x1611Fu, 0x16127u),HB_CODEPOINT_ENCODE3 (0x16129u, 0x1611Fu, 0x16124u), - HB_CODEPOINT_ENCODE3 (0x16D63u, 0x16D67u, 0x16D69u),HB_CODEPOINT_ENCODE3 (0x16D67u, 0x16D67u, 0x16D68u), - HB_CODEPOINT_ENCODE3 (0x16D69u, 0x16D67u, 0x16D6Au), HB_CODEPOINT_ENCODE3 (0x1D157u, 0x1D165u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D158u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Eu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Fu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D170u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D171u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D172u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D1B9u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BAu, 0x1D165u, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Fu, 0x0000u), - HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Fu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D0, 0x05B7, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D0, 0x05B8, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D0, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D1, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D1, 0x05BF, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D2, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D3, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D4, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D5, 0x05B9, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D5, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D6, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D8, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05D9, 0x05B4, 0x0000), HB_CODEPOINT_ENCODE3 (0x05D9, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05DA, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05DB, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05DB, 0x05BF, 0x0000), HB_CODEPOINT_ENCODE3 (0x05DC, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05DE, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E0, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E1, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E3, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E4, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E4, 0x05BF, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E6, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E7, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E8, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E9, 0x05BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05E9, 0x05C1, 0x0000), HB_CODEPOINT_ENCODE3 (0x05E9, 0x05C2, 0x0000), + HB_CODEPOINT_ENCODE3 (0x05EA, 0x05BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x05F2, 0x05B7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0627, 0x0653, 0x0622), HB_CODEPOINT_ENCODE3 (0x0627, 0x0654, 0x0623), + HB_CODEPOINT_ENCODE3 (0x0627, 0x0655, 0x0625), HB_CODEPOINT_ENCODE3 (0x0648, 0x0654, 0x0624), + HB_CODEPOINT_ENCODE3 (0x064A, 0x0654, 0x0626), HB_CODEPOINT_ENCODE3 (0x06C1, 0x0654, 0x06C2), + HB_CODEPOINT_ENCODE3 (0x06D2, 0x0654, 0x06D3), HB_CODEPOINT_ENCODE3 (0x06D5, 0x0654, 0x06C0), + HB_CODEPOINT_ENCODE3 (0x0915, 0x093C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0916, 0x093C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0917, 0x093C, 0x0000), HB_CODEPOINT_ENCODE3 (0x091C, 0x093C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0921, 0x093C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0922, 0x093C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0928, 0x093C, 0x0929), HB_CODEPOINT_ENCODE3 (0x092B, 0x093C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x092F, 0x093C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0930, 0x093C, 0x0931), + HB_CODEPOINT_ENCODE3 (0x0933, 0x093C, 0x0934), HB_CODEPOINT_ENCODE3 (0x09A1, 0x09BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x09A2, 0x09BC, 0x0000), HB_CODEPOINT_ENCODE3 (0x09AF, 0x09BC, 0x0000), + HB_CODEPOINT_ENCODE3 (0x09C7, 0x09BE, 0x09CB), HB_CODEPOINT_ENCODE3 (0x09C7, 0x09D7, 0x09CC), + HB_CODEPOINT_ENCODE3 (0x0A16, 0x0A3C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0A17, 0x0A3C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0A1C, 0x0A3C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0A2B, 0x0A3C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0A32, 0x0A3C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0A38, 0x0A3C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0B21, 0x0B3C, 0x0000), HB_CODEPOINT_ENCODE3 (0x0B22, 0x0B3C, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0B47, 0x0B3E, 0x0B4B), HB_CODEPOINT_ENCODE3 (0x0B47, 0x0B56, 0x0B48), + HB_CODEPOINT_ENCODE3 (0x0B47, 0x0B57, 0x0B4C), HB_CODEPOINT_ENCODE3 (0x0B92, 0x0BD7, 0x0B94), + HB_CODEPOINT_ENCODE3 (0x0BC6, 0x0BBE, 0x0BCA), HB_CODEPOINT_ENCODE3 (0x0BC6, 0x0BD7, 0x0BCC), + HB_CODEPOINT_ENCODE3 (0x0BC7, 0x0BBE, 0x0BCB), HB_CODEPOINT_ENCODE3 (0x0C46, 0x0C56, 0x0C48), + HB_CODEPOINT_ENCODE3 (0x0CBF, 0x0CD5, 0x0CC0), HB_CODEPOINT_ENCODE3 (0x0CC6, 0x0CC2, 0x0CCA), + HB_CODEPOINT_ENCODE3 (0x0CC6, 0x0CD5, 0x0CC7), HB_CODEPOINT_ENCODE3 (0x0CC6, 0x0CD6, 0x0CC8), + HB_CODEPOINT_ENCODE3 (0x0CCA, 0x0CD5, 0x0CCB), HB_CODEPOINT_ENCODE3 (0x0D46, 0x0D3E, 0x0D4A), + HB_CODEPOINT_ENCODE3 (0x0D46, 0x0D57, 0x0D4C), HB_CODEPOINT_ENCODE3 (0x0D47, 0x0D3E, 0x0D4B), + HB_CODEPOINT_ENCODE3 (0x0DD9, 0x0DCA, 0x0DDA), HB_CODEPOINT_ENCODE3 (0x0DD9, 0x0DCF, 0x0DDC), + HB_CODEPOINT_ENCODE3 (0x0DD9, 0x0DDF, 0x0DDE), HB_CODEPOINT_ENCODE3 (0x0DDC, 0x0DCA, 0x0DDD), + HB_CODEPOINT_ENCODE3 (0x0F40, 0x0FB5, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F42, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F4C, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F51, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F56, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F5B, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F71, 0x0F72, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F71, 0x0F74, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F71, 0x0F80, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F90, 0x0FB5, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0F92, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0F9C, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0FA1, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0FA6, 0x0FB7, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0FAB, 0x0FB7, 0x0000), HB_CODEPOINT_ENCODE3 (0x0FB2, 0x0F80, 0x0000), + HB_CODEPOINT_ENCODE3 (0x0FB3, 0x0F80, 0x0000), HB_CODEPOINT_ENCODE3 (0x1025, 0x102E, 0x1026), + HB_CODEPOINT_ENCODE3 (0x1B05, 0x1B35, 0x1B06), HB_CODEPOINT_ENCODE3 (0x1B07, 0x1B35, 0x1B08), + HB_CODEPOINT_ENCODE3 (0x1B09, 0x1B35, 0x1B0A), HB_CODEPOINT_ENCODE3 (0x1B0B, 0x1B35, 0x1B0C), + HB_CODEPOINT_ENCODE3 (0x1B0D, 0x1B35, 0x1B0E), HB_CODEPOINT_ENCODE3 (0x1B11, 0x1B35, 0x1B12), + HB_CODEPOINT_ENCODE3 (0x1B3A, 0x1B35, 0x1B3B), HB_CODEPOINT_ENCODE3 (0x1B3C, 0x1B35, 0x1B3D), + HB_CODEPOINT_ENCODE3 (0x1B3E, 0x1B35, 0x1B40), HB_CODEPOINT_ENCODE3 (0x1B3F, 0x1B35, 0x1B41), + HB_CODEPOINT_ENCODE3 (0x1B42, 0x1B35, 0x1B43), HB_CODEPOINT_ENCODE3 (0x1E36, 0x0304, 0x1E38), + HB_CODEPOINT_ENCODE3 (0x1E37, 0x0304, 0x1E39), HB_CODEPOINT_ENCODE3 (0x1E5A, 0x0304, 0x1E5C), + HB_CODEPOINT_ENCODE3 (0x1E5B, 0x0304, 0x1E5D), HB_CODEPOINT_ENCODE3 (0x1E62, 0x0307, 0x1E68), + HB_CODEPOINT_ENCODE3 (0x1E63, 0x0307, 0x1E69), HB_CODEPOINT_ENCODE3 (0x1EA0, 0x0302, 0x1EAC), + HB_CODEPOINT_ENCODE3 (0x1EA0, 0x0306, 0x1EB6), HB_CODEPOINT_ENCODE3 (0x1EA1, 0x0302, 0x1EAD), + HB_CODEPOINT_ENCODE3 (0x1EA1, 0x0306, 0x1EB7), HB_CODEPOINT_ENCODE3 (0x1EB8, 0x0302, 0x1EC6), + HB_CODEPOINT_ENCODE3 (0x1EB9, 0x0302, 0x1EC7), HB_CODEPOINT_ENCODE3 (0x1ECC, 0x0302, 0x1ED8), + HB_CODEPOINT_ENCODE3 (0x1ECD, 0x0302, 0x1ED9), HB_CODEPOINT_ENCODE3 (0x1F00, 0x0300, 0x1F02), + HB_CODEPOINT_ENCODE3 (0x1F00, 0x0301, 0x1F04), HB_CODEPOINT_ENCODE3 (0x1F00, 0x0342, 0x1F06), + HB_CODEPOINT_ENCODE3 (0x1F00, 0x0345, 0x1F80), HB_CODEPOINT_ENCODE3 (0x1F01, 0x0300, 0x1F03), + HB_CODEPOINT_ENCODE3 (0x1F01, 0x0301, 0x1F05), HB_CODEPOINT_ENCODE3 (0x1F01, 0x0342, 0x1F07), + HB_CODEPOINT_ENCODE3 (0x1F01, 0x0345, 0x1F81), HB_CODEPOINT_ENCODE3 (0x1F02, 0x0345, 0x1F82), + HB_CODEPOINT_ENCODE3 (0x1F03, 0x0345, 0x1F83), HB_CODEPOINT_ENCODE3 (0x1F04, 0x0345, 0x1F84), + HB_CODEPOINT_ENCODE3 (0x1F05, 0x0345, 0x1F85), HB_CODEPOINT_ENCODE3 (0x1F06, 0x0345, 0x1F86), + HB_CODEPOINT_ENCODE3 (0x1F07, 0x0345, 0x1F87), HB_CODEPOINT_ENCODE3 (0x1F08, 0x0300, 0x1F0A), + HB_CODEPOINT_ENCODE3 (0x1F08, 0x0301, 0x1F0C), HB_CODEPOINT_ENCODE3 (0x1F08, 0x0342, 0x1F0E), + HB_CODEPOINT_ENCODE3 (0x1F08, 0x0345, 0x1F88), HB_CODEPOINT_ENCODE3 (0x1F09, 0x0300, 0x1F0B), + HB_CODEPOINT_ENCODE3 (0x1F09, 0x0301, 0x1F0D), HB_CODEPOINT_ENCODE3 (0x1F09, 0x0342, 0x1F0F), + HB_CODEPOINT_ENCODE3 (0x1F09, 0x0345, 0x1F89), HB_CODEPOINT_ENCODE3 (0x1F0A, 0x0345, 0x1F8A), + HB_CODEPOINT_ENCODE3 (0x1F0B, 0x0345, 0x1F8B), HB_CODEPOINT_ENCODE3 (0x1F0C, 0x0345, 0x1F8C), + HB_CODEPOINT_ENCODE3 (0x1F0D, 0x0345, 0x1F8D), HB_CODEPOINT_ENCODE3 (0x1F0E, 0x0345, 0x1F8E), + HB_CODEPOINT_ENCODE3 (0x1F0F, 0x0345, 0x1F8F), HB_CODEPOINT_ENCODE3 (0x1F10, 0x0300, 0x1F12), + HB_CODEPOINT_ENCODE3 (0x1F10, 0x0301, 0x1F14), HB_CODEPOINT_ENCODE3 (0x1F11, 0x0300, 0x1F13), + HB_CODEPOINT_ENCODE3 (0x1F11, 0x0301, 0x1F15), HB_CODEPOINT_ENCODE3 (0x1F18, 0x0300, 0x1F1A), + HB_CODEPOINT_ENCODE3 (0x1F18, 0x0301, 0x1F1C), HB_CODEPOINT_ENCODE3 (0x1F19, 0x0300, 0x1F1B), + HB_CODEPOINT_ENCODE3 (0x1F19, 0x0301, 0x1F1D), HB_CODEPOINT_ENCODE3 (0x1F20, 0x0300, 0x1F22), + HB_CODEPOINT_ENCODE3 (0x1F20, 0x0301, 0x1F24), HB_CODEPOINT_ENCODE3 (0x1F20, 0x0342, 0x1F26), + HB_CODEPOINT_ENCODE3 (0x1F20, 0x0345, 0x1F90), HB_CODEPOINT_ENCODE3 (0x1F21, 0x0300, 0x1F23), + HB_CODEPOINT_ENCODE3 (0x1F21, 0x0301, 0x1F25), HB_CODEPOINT_ENCODE3 (0x1F21, 0x0342, 0x1F27), + HB_CODEPOINT_ENCODE3 (0x1F21, 0x0345, 0x1F91), HB_CODEPOINT_ENCODE3 (0x1F22, 0x0345, 0x1F92), + HB_CODEPOINT_ENCODE3 (0x1F23, 0x0345, 0x1F93), HB_CODEPOINT_ENCODE3 (0x1F24, 0x0345, 0x1F94), + HB_CODEPOINT_ENCODE3 (0x1F25, 0x0345, 0x1F95), HB_CODEPOINT_ENCODE3 (0x1F26, 0x0345, 0x1F96), + HB_CODEPOINT_ENCODE3 (0x1F27, 0x0345, 0x1F97), HB_CODEPOINT_ENCODE3 (0x1F28, 0x0300, 0x1F2A), + HB_CODEPOINT_ENCODE3 (0x1F28, 0x0301, 0x1F2C), HB_CODEPOINT_ENCODE3 (0x1F28, 0x0342, 0x1F2E), + HB_CODEPOINT_ENCODE3 (0x1F28, 0x0345, 0x1F98), HB_CODEPOINT_ENCODE3 (0x1F29, 0x0300, 0x1F2B), + HB_CODEPOINT_ENCODE3 (0x1F29, 0x0301, 0x1F2D), HB_CODEPOINT_ENCODE3 (0x1F29, 0x0342, 0x1F2F), + HB_CODEPOINT_ENCODE3 (0x1F29, 0x0345, 0x1F99), HB_CODEPOINT_ENCODE3 (0x1F2A, 0x0345, 0x1F9A), + HB_CODEPOINT_ENCODE3 (0x1F2B, 0x0345, 0x1F9B), HB_CODEPOINT_ENCODE3 (0x1F2C, 0x0345, 0x1F9C), + HB_CODEPOINT_ENCODE3 (0x1F2D, 0x0345, 0x1F9D), HB_CODEPOINT_ENCODE3 (0x1F2E, 0x0345, 0x1F9E), + HB_CODEPOINT_ENCODE3 (0x1F2F, 0x0345, 0x1F9F), HB_CODEPOINT_ENCODE3 (0x1F30, 0x0300, 0x1F32), + HB_CODEPOINT_ENCODE3 (0x1F30, 0x0301, 0x1F34), HB_CODEPOINT_ENCODE3 (0x1F30, 0x0342, 0x1F36), + HB_CODEPOINT_ENCODE3 (0x1F31, 0x0300, 0x1F33), HB_CODEPOINT_ENCODE3 (0x1F31, 0x0301, 0x1F35), + HB_CODEPOINT_ENCODE3 (0x1F31, 0x0342, 0x1F37), HB_CODEPOINT_ENCODE3 (0x1F38, 0x0300, 0x1F3A), + HB_CODEPOINT_ENCODE3 (0x1F38, 0x0301, 0x1F3C), HB_CODEPOINT_ENCODE3 (0x1F38, 0x0342, 0x1F3E), + HB_CODEPOINT_ENCODE3 (0x1F39, 0x0300, 0x1F3B), HB_CODEPOINT_ENCODE3 (0x1F39, 0x0301, 0x1F3D), + HB_CODEPOINT_ENCODE3 (0x1F39, 0x0342, 0x1F3F), HB_CODEPOINT_ENCODE3 (0x1F40, 0x0300, 0x1F42), + HB_CODEPOINT_ENCODE3 (0x1F40, 0x0301, 0x1F44), HB_CODEPOINT_ENCODE3 (0x1F41, 0x0300, 0x1F43), + HB_CODEPOINT_ENCODE3 (0x1F41, 0x0301, 0x1F45), HB_CODEPOINT_ENCODE3 (0x1F48, 0x0300, 0x1F4A), + HB_CODEPOINT_ENCODE3 (0x1F48, 0x0301, 0x1F4C), HB_CODEPOINT_ENCODE3 (0x1F49, 0x0300, 0x1F4B), + HB_CODEPOINT_ENCODE3 (0x1F49, 0x0301, 0x1F4D), HB_CODEPOINT_ENCODE3 (0x1F50, 0x0300, 0x1F52), + HB_CODEPOINT_ENCODE3 (0x1F50, 0x0301, 0x1F54), HB_CODEPOINT_ENCODE3 (0x1F50, 0x0342, 0x1F56), + HB_CODEPOINT_ENCODE3 (0x1F51, 0x0300, 0x1F53), HB_CODEPOINT_ENCODE3 (0x1F51, 0x0301, 0x1F55), + HB_CODEPOINT_ENCODE3 (0x1F51, 0x0342, 0x1F57), HB_CODEPOINT_ENCODE3 (0x1F59, 0x0300, 0x1F5B), + HB_CODEPOINT_ENCODE3 (0x1F59, 0x0301, 0x1F5D), HB_CODEPOINT_ENCODE3 (0x1F59, 0x0342, 0x1F5F), + HB_CODEPOINT_ENCODE3 (0x1F60, 0x0300, 0x1F62), HB_CODEPOINT_ENCODE3 (0x1F60, 0x0301, 0x1F64), + HB_CODEPOINT_ENCODE3 (0x1F60, 0x0342, 0x1F66), HB_CODEPOINT_ENCODE3 (0x1F60, 0x0345, 0x1FA0), + HB_CODEPOINT_ENCODE3 (0x1F61, 0x0300, 0x1F63), HB_CODEPOINT_ENCODE3 (0x1F61, 0x0301, 0x1F65), + HB_CODEPOINT_ENCODE3 (0x1F61, 0x0342, 0x1F67), HB_CODEPOINT_ENCODE3 (0x1F61, 0x0345, 0x1FA1), + HB_CODEPOINT_ENCODE3 (0x1F62, 0x0345, 0x1FA2), HB_CODEPOINT_ENCODE3 (0x1F63, 0x0345, 0x1FA3), + HB_CODEPOINT_ENCODE3 (0x1F64, 0x0345, 0x1FA4), HB_CODEPOINT_ENCODE3 (0x1F65, 0x0345, 0x1FA5), + HB_CODEPOINT_ENCODE3 (0x1F66, 0x0345, 0x1FA6), HB_CODEPOINT_ENCODE3 (0x1F67, 0x0345, 0x1FA7), + HB_CODEPOINT_ENCODE3 (0x1F68, 0x0300, 0x1F6A), HB_CODEPOINT_ENCODE3 (0x1F68, 0x0301, 0x1F6C), + HB_CODEPOINT_ENCODE3 (0x1F68, 0x0342, 0x1F6E), HB_CODEPOINT_ENCODE3 (0x1F68, 0x0345, 0x1FA8), + HB_CODEPOINT_ENCODE3 (0x1F69, 0x0300, 0x1F6B), HB_CODEPOINT_ENCODE3 (0x1F69, 0x0301, 0x1F6D), + HB_CODEPOINT_ENCODE3 (0x1F69, 0x0342, 0x1F6F), HB_CODEPOINT_ENCODE3 (0x1F69, 0x0345, 0x1FA9), + HB_CODEPOINT_ENCODE3 (0x1F6A, 0x0345, 0x1FAA), HB_CODEPOINT_ENCODE3 (0x1F6B, 0x0345, 0x1FAB), + HB_CODEPOINT_ENCODE3 (0x1F6C, 0x0345, 0x1FAC), HB_CODEPOINT_ENCODE3 (0x1F6D, 0x0345, 0x1FAD), + HB_CODEPOINT_ENCODE3 (0x1F6E, 0x0345, 0x1FAE), HB_CODEPOINT_ENCODE3 (0x1F6F, 0x0345, 0x1FAF), + HB_CODEPOINT_ENCODE3 (0x1F70, 0x0345, 0x1FB2), HB_CODEPOINT_ENCODE3 (0x1F74, 0x0345, 0x1FC2), + HB_CODEPOINT_ENCODE3 (0x1F7C, 0x0345, 0x1FF2), HB_CODEPOINT_ENCODE3 (0x1FB6, 0x0345, 0x1FB7), + HB_CODEPOINT_ENCODE3 (0x1FBF, 0x0300, 0x1FCD), HB_CODEPOINT_ENCODE3 (0x1FBF, 0x0301, 0x1FCE), + HB_CODEPOINT_ENCODE3 (0x1FBF, 0x0342, 0x1FCF), HB_CODEPOINT_ENCODE3 (0x1FC6, 0x0345, 0x1FC7), + HB_CODEPOINT_ENCODE3 (0x1FF6, 0x0345, 0x1FF7), HB_CODEPOINT_ENCODE3 (0x1FFE, 0x0300, 0x1FDD), + HB_CODEPOINT_ENCODE3 (0x1FFE, 0x0301, 0x1FDE), HB_CODEPOINT_ENCODE3 (0x1FFE, 0x0342, 0x1FDF), + HB_CODEPOINT_ENCODE3 (0x2190, 0x0338, 0x219A), HB_CODEPOINT_ENCODE3 (0x2192, 0x0338, 0x219B), + HB_CODEPOINT_ENCODE3 (0x2194, 0x0338, 0x21AE), HB_CODEPOINT_ENCODE3 (0x21D0, 0x0338, 0x21CD), + HB_CODEPOINT_ENCODE3 (0x21D2, 0x0338, 0x21CF), HB_CODEPOINT_ENCODE3 (0x21D4, 0x0338, 0x21CE), + HB_CODEPOINT_ENCODE3 (0x2203, 0x0338, 0x2204), HB_CODEPOINT_ENCODE3 (0x2208, 0x0338, 0x2209), + HB_CODEPOINT_ENCODE3 (0x220B, 0x0338, 0x220C), HB_CODEPOINT_ENCODE3 (0x2223, 0x0338, 0x2224), + HB_CODEPOINT_ENCODE3 (0x2225, 0x0338, 0x2226), HB_CODEPOINT_ENCODE3 (0x223C, 0x0338, 0x2241), + HB_CODEPOINT_ENCODE3 (0x2243, 0x0338, 0x2244), HB_CODEPOINT_ENCODE3 (0x2245, 0x0338, 0x2247), + HB_CODEPOINT_ENCODE3 (0x2248, 0x0338, 0x2249), HB_CODEPOINT_ENCODE3 (0x224D, 0x0338, 0x226D), + HB_CODEPOINT_ENCODE3 (0x2261, 0x0338, 0x2262), HB_CODEPOINT_ENCODE3 (0x2264, 0x0338, 0x2270), + HB_CODEPOINT_ENCODE3 (0x2265, 0x0338, 0x2271), HB_CODEPOINT_ENCODE3 (0x2272, 0x0338, 0x2274), + HB_CODEPOINT_ENCODE3 (0x2273, 0x0338, 0x2275), HB_CODEPOINT_ENCODE3 (0x2276, 0x0338, 0x2278), + HB_CODEPOINT_ENCODE3 (0x2277, 0x0338, 0x2279), HB_CODEPOINT_ENCODE3 (0x227A, 0x0338, 0x2280), + HB_CODEPOINT_ENCODE3 (0x227B, 0x0338, 0x2281), HB_CODEPOINT_ENCODE3 (0x227C, 0x0338, 0x22E0), + HB_CODEPOINT_ENCODE3 (0x227D, 0x0338, 0x22E1), HB_CODEPOINT_ENCODE3 (0x2282, 0x0338, 0x2284), + HB_CODEPOINT_ENCODE3 (0x2283, 0x0338, 0x2285), HB_CODEPOINT_ENCODE3 (0x2286, 0x0338, 0x2288), + HB_CODEPOINT_ENCODE3 (0x2287, 0x0338, 0x2289), HB_CODEPOINT_ENCODE3 (0x2291, 0x0338, 0x22E2), + HB_CODEPOINT_ENCODE3 (0x2292, 0x0338, 0x22E3), HB_CODEPOINT_ENCODE3 (0x22A2, 0x0338, 0x22AC), + HB_CODEPOINT_ENCODE3 (0x22A8, 0x0338, 0x22AD), HB_CODEPOINT_ENCODE3 (0x22A9, 0x0338, 0x22AE), + HB_CODEPOINT_ENCODE3 (0x22AB, 0x0338, 0x22AF), HB_CODEPOINT_ENCODE3 (0x22B2, 0x0338, 0x22EA), + HB_CODEPOINT_ENCODE3 (0x22B3, 0x0338, 0x22EB), HB_CODEPOINT_ENCODE3 (0x22B4, 0x0338, 0x22EC), + HB_CODEPOINT_ENCODE3 (0x22B5, 0x0338, 0x22ED), HB_CODEPOINT_ENCODE3 (0x2ADD, 0x0338, 0x0000), + HB_CODEPOINT_ENCODE3 (0x3046, 0x3099, 0x3094), HB_CODEPOINT_ENCODE3 (0x304B, 0x3099, 0x304C), + HB_CODEPOINT_ENCODE3 (0x304D, 0x3099, 0x304E), HB_CODEPOINT_ENCODE3 (0x304F, 0x3099, 0x3050), + HB_CODEPOINT_ENCODE3 (0x3051, 0x3099, 0x3052), HB_CODEPOINT_ENCODE3 (0x3053, 0x3099, 0x3054), + HB_CODEPOINT_ENCODE3 (0x3055, 0x3099, 0x3056), HB_CODEPOINT_ENCODE3 (0x3057, 0x3099, 0x3058), + HB_CODEPOINT_ENCODE3 (0x3059, 0x3099, 0x305A), HB_CODEPOINT_ENCODE3 (0x305B, 0x3099, 0x305C), + HB_CODEPOINT_ENCODE3 (0x305D, 0x3099, 0x305E), HB_CODEPOINT_ENCODE3 (0x305F, 0x3099, 0x3060), + HB_CODEPOINT_ENCODE3 (0x3061, 0x3099, 0x3062), HB_CODEPOINT_ENCODE3 (0x3064, 0x3099, 0x3065), + HB_CODEPOINT_ENCODE3 (0x3066, 0x3099, 0x3067), HB_CODEPOINT_ENCODE3 (0x3068, 0x3099, 0x3069), + HB_CODEPOINT_ENCODE3 (0x306F, 0x3099, 0x3070), HB_CODEPOINT_ENCODE3 (0x306F, 0x309A, 0x3071), + HB_CODEPOINT_ENCODE3 (0x3072, 0x3099, 0x3073), HB_CODEPOINT_ENCODE3 (0x3072, 0x309A, 0x3074), + HB_CODEPOINT_ENCODE3 (0x3075, 0x3099, 0x3076), HB_CODEPOINT_ENCODE3 (0x3075, 0x309A, 0x3077), + HB_CODEPOINT_ENCODE3 (0x3078, 0x3099, 0x3079), HB_CODEPOINT_ENCODE3 (0x3078, 0x309A, 0x307A), + HB_CODEPOINT_ENCODE3 (0x307B, 0x3099, 0x307C), HB_CODEPOINT_ENCODE3 (0x307B, 0x309A, 0x307D), + HB_CODEPOINT_ENCODE3 (0x309D, 0x3099, 0x309E), HB_CODEPOINT_ENCODE3 (0x30A6, 0x3099, 0x30F4), + HB_CODEPOINT_ENCODE3 (0x30AB, 0x3099, 0x30AC), HB_CODEPOINT_ENCODE3 (0x30AD, 0x3099, 0x30AE), + HB_CODEPOINT_ENCODE3 (0x30AF, 0x3099, 0x30B0), HB_CODEPOINT_ENCODE3 (0x30B1, 0x3099, 0x30B2), + HB_CODEPOINT_ENCODE3 (0x30B3, 0x3099, 0x30B4), HB_CODEPOINT_ENCODE3 (0x30B5, 0x3099, 0x30B6), + HB_CODEPOINT_ENCODE3 (0x30B7, 0x3099, 0x30B8), HB_CODEPOINT_ENCODE3 (0x30B9, 0x3099, 0x30BA), + HB_CODEPOINT_ENCODE3 (0x30BB, 0x3099, 0x30BC), HB_CODEPOINT_ENCODE3 (0x30BD, 0x3099, 0x30BE), + HB_CODEPOINT_ENCODE3 (0x30BF, 0x3099, 0x30C0), HB_CODEPOINT_ENCODE3 (0x30C1, 0x3099, 0x30C2), + HB_CODEPOINT_ENCODE3 (0x30C4, 0x3099, 0x30C5), HB_CODEPOINT_ENCODE3 (0x30C6, 0x3099, 0x30C7), + HB_CODEPOINT_ENCODE3 (0x30C8, 0x3099, 0x30C9), HB_CODEPOINT_ENCODE3 (0x30CF, 0x3099, 0x30D0), + HB_CODEPOINT_ENCODE3 (0x30CF, 0x309A, 0x30D1), HB_CODEPOINT_ENCODE3 (0x30D2, 0x3099, 0x30D3), + HB_CODEPOINT_ENCODE3 (0x30D2, 0x309A, 0x30D4), HB_CODEPOINT_ENCODE3 (0x30D5, 0x3099, 0x30D6), + HB_CODEPOINT_ENCODE3 (0x30D5, 0x309A, 0x30D7), HB_CODEPOINT_ENCODE3 (0x30D8, 0x3099, 0x30D9), + HB_CODEPOINT_ENCODE3 (0x30D8, 0x309A, 0x30DA), HB_CODEPOINT_ENCODE3 (0x30DB, 0x3099, 0x30DC), + HB_CODEPOINT_ENCODE3 (0x30DB, 0x309A, 0x30DD), HB_CODEPOINT_ENCODE3 (0x30EF, 0x3099, 0x30F7), + HB_CODEPOINT_ENCODE3 (0x30F0, 0x3099, 0x30F8), HB_CODEPOINT_ENCODE3 (0x30F1, 0x3099, 0x30F9), + HB_CODEPOINT_ENCODE3 (0x30F2, 0x3099, 0x30FA), HB_CODEPOINT_ENCODE3 (0x30FD, 0x3099, 0x30FE), + HB_CODEPOINT_ENCODE3 (0xFB49, 0x05C1, 0x0000), HB_CODEPOINT_ENCODE3 (0xFB49, 0x05C2, 0x0000), + HB_CODEPOINT_ENCODE3 (0x105D2, 0x0307, 0x105C9), HB_CODEPOINT_ENCODE3 (0x105DA, 0x0307, 0x105E4), + HB_CODEPOINT_ENCODE3 (0x11099, 0x110BA, 0x1109A),HB_CODEPOINT_ENCODE3 (0x1109B, 0x110BA, 0x1109C), + HB_CODEPOINT_ENCODE3 (0x110A5, 0x110BA, 0x110AB),HB_CODEPOINT_ENCODE3 (0x11131, 0x11127, 0x1112E), + HB_CODEPOINT_ENCODE3 (0x11132, 0x11127, 0x1112F),HB_CODEPOINT_ENCODE3 (0x11347, 0x1133E, 0x1134B), + HB_CODEPOINT_ENCODE3 (0x11347, 0x11357, 0x1134C),HB_CODEPOINT_ENCODE3 (0x11382, 0x113C9, 0x11383), + HB_CODEPOINT_ENCODE3 (0x11384, 0x113BB, 0x11385),HB_CODEPOINT_ENCODE3 (0x1138B, 0x113C2, 0x1138E), + HB_CODEPOINT_ENCODE3 (0x11390, 0x113C9, 0x11391),HB_CODEPOINT_ENCODE3 (0x113C2, 0x113B8, 0x113C7), + HB_CODEPOINT_ENCODE3 (0x113C2, 0x113C2, 0x113C5),HB_CODEPOINT_ENCODE3 (0x113C2, 0x113C9, 0x113C8), + HB_CODEPOINT_ENCODE3 (0x114B9, 0x114B0, 0x114BC),HB_CODEPOINT_ENCODE3 (0x114B9, 0x114BA, 0x114BB), + HB_CODEPOINT_ENCODE3 (0x114B9, 0x114BD, 0x114BE),HB_CODEPOINT_ENCODE3 (0x115B8, 0x115AF, 0x115BA), + HB_CODEPOINT_ENCODE3 (0x115B9, 0x115AF, 0x115BB),HB_CODEPOINT_ENCODE3 (0x11935, 0x11930, 0x11938), + HB_CODEPOINT_ENCODE3 (0x1611E, 0x1611E, 0x16121),HB_CODEPOINT_ENCODE3 (0x1611E, 0x1611F, 0x16123), + HB_CODEPOINT_ENCODE3 (0x1611E, 0x16120, 0x16125),HB_CODEPOINT_ENCODE3 (0x1611E, 0x16129, 0x16122), + HB_CODEPOINT_ENCODE3 (0x16121, 0x1611F, 0x16126),HB_CODEPOINT_ENCODE3 (0x16121, 0x16120, 0x16128), + HB_CODEPOINT_ENCODE3 (0x16122, 0x1611F, 0x16127),HB_CODEPOINT_ENCODE3 (0x16129, 0x1611F, 0x16124), + HB_CODEPOINT_ENCODE3 (0x16D63, 0x16D67, 0x16D69),HB_CODEPOINT_ENCODE3 (0x16D67, 0x16D67, 0x16D68), + HB_CODEPOINT_ENCODE3 (0x16D69, 0x16D67, 0x16D6A), HB_CODEPOINT_ENCODE3 (0x1D157, 0x1D165, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D158, 0x1D165, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D16E, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D16F, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D170, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D171, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D15F, 0x1D172, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D1B9, 0x1D165, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D1BA, 0x1D165, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D1BB, 0x1D16E, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D1BB, 0x1D16F, 0x0000), + HB_CODEPOINT_ENCODE3 (0x1D1BC, 0x1D16E, 0x0000), HB_CODEPOINT_ENCODE3 (0x1D1BC, 0x1D16F, 0x0000), }; #ifndef HB_OPTIMIZE_SIZE -static const uint8_t -_hb_ucd_u8[17612] = +#include + +static const uint8_t _hb_ucd_u8[19868]= { - 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 18, 19, 20, 21, 22, 23, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 25, 26, 5, 27, 28, - 5, 29, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 31, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 32, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 33, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 17, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 33, 41, 42, 43, 44, 45, - 46, 47, 48, 39, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 49, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 50, 17, 17, 17, 51, 17, 52, 53, 54, 55, 56, 57, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 58, 59, 59, 59, 59, 59, 59, 59, 59, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 17, 61, 62, 17, 63, 64, 65, - 66, 67, 68, 69, 70, 71, 17, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, - 17, 17, 17, 97, 98, 99,100,100,100,100,100,100,100,100,100,101, - 17, 17, 17, 17,102, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,103, 17, 17,104,100,100,100,100,100,100,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,105,100,100,100,100,100,100, 17, 17,106,107,100,108,109,110, - 17, 17, 17, 17, 17, 17, 17,111, 17, 17, 17, 17,112,113,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,114, - 17,115,116,100,100,100,100,100,100,100,100,100,117,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,118, 39,119,120, - 121,122,123,124,125,126,127,128, 39, 39,129,100,100,100,100,130, - 131,132,133,100,134,135,100,136,137,138,100,100,139,140,141,100, - 142,143,144,145, 39, 39,146,147,148, 39,149,150,100,100,100,100, - 17, 17, 17, 17, 17, 17,151, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17,152,153, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,155, 17, 17,156,100, - 100,100,100,100,100,100,100,100, 17, 17,157,100,100,100,100,100, - 17, 17, 17,158, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,159,100,100,100,100,100,100,100,100,100,100,100,100, - 160,161,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,162, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,163, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 28, 26, 29, 30, 31, 32, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 33, 34, 34, 34, 34, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 26, 56, 57, 58, 58, 58, 58, 59, 26, 26, 60, 26, 26, 26, 26, 26, + 26, 61, 26, 62, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 63, 58, 58, 58, 26, 64, 65, 66, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 67, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 68, 69, 70, 58, 58, 58, 58, 71, 58, + 58, 58, 58, 58, 58, 58, 72, 73, 74, 75, 76, 77, 78, 79, 58, 80, + 81, 82, 83, 84, 85, 58, 86, 87, 88, 89, 78, 90, 91, 92, 58, 58, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 93, 26, 26, 26, 26, 26, 26, 26, 26, 94, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 95, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 96, 26, 97, 58, 58, 58, 58, 26, 98, 58, 58, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 99, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,100, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 101, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,102, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,103, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, @@ -1148,496 +914,529 @@ _hb_ucd_u8[17612] = 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, - 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, - 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, - 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, - 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, - 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, - 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, - 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, - 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, - 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, - 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, - 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, - 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, - 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, - 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, - 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, - 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 57, 43, 43, 43, 43, - 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, - 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, - 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, - 36, 43, 2, 7, 7, 7, 7, 7, 88, 36, 36, 36, 36, 36, 36, 36, - 70, 86, 62, 36, 36, 36, 61, 62, 61, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 61, 36, 36, 36, 61, 61, 44, 36, 36, 44, 71, 86, - 87, 43, 80, 89, 90, 89, 87, 61, 44, 44, 44, 89, 44, 44, 36, 62, - 36, 43, 44, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 56, 63, 80, - 57, 85, 62, 36, 36, 61, 44, 62, 61, 36, 62, 61, 36, 44, 80, 86, - 87, 80, 44, 57, 80, 57, 43, 44, 57, 44, 44, 44, 62, 36, 61, 61, - 44, 44, 44, 7, 7, 7, 7, 7, 43, 36, 70, 64, 44, 44, 44, 44, - 57, 85, 62, 36, 36, 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, - 61, 36, 62, 36, 36, 44, 71, 86, 87, 43, 43, 57, 85, 89, 87, 44, - 61, 44, 44, 44, 44, 44, 44, 44, 66, 44, 44, 44, 62, 43, 43, 43, - 57, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 44, 71, 87, - 87, 43, 80, 89, 90, 89, 87, 44, 44, 44, 57, 85, 44, 44, 36, 62, - 78, 27, 27, 27, 44, 44, 44, 44, 44, 71, 62, 36, 36, 61, 44, 36, - 61, 36, 36, 44, 62, 61, 61, 36, 44, 62, 61, 44, 36, 61, 44, 36, - 36, 36, 36, 36, 36, 44, 44, 86, 85, 90, 44, 86, 90, 86, 87, 44, - 61, 44, 44, 89, 44, 44, 44, 44, 27, 91, 67, 67, 56, 92, 44, 44, - 85, 86, 71, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 44, 71, 43, 85, 86, 90, 43, 80, 43, 43, 44, - 44, 44, 57, 80, 36, 61, 62, 44, 44, 44, 44, 93, 27, 27, 27, 91, - 70, 86, 72, 36, 36, 36, 61, 36, 36, 36, 62, 36, 36, 44, 71, 87, - 86, 86, 90, 85, 90, 86, 43, 44, 44, 44, 89, 90, 44, 44, 62, 61, - 62, 94, 44, 44, 44, 44, 44, 44, 43, 86, 36, 36, 36, 36, 61, 36, - 36, 36, 36, 36, 36, 70, 71, 86, 87, 43, 80, 86, 90, 86, 87, 77, - 44, 44, 36, 94, 27, 27, 27, 95, 27, 27, 27, 27, 91, 36, 36, 36, - 57, 86, 62, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 36, 36, 36, - 36, 62, 36, 36, 36, 36, 62, 44, 36, 36, 36, 61, 44, 80, 44, 89, - 86, 43, 80, 80, 86, 86, 86, 86, 44, 86, 64, 44, 44, 44, 44, 44, - 62, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 96, - 36, 36, 36, 75, 43, 43, 43, 60, 7, 7, 7, 7, 7, 2, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, - 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, - 36, 36, 61, 81, 43, 43, 43, 80, 7, 7, 7, 7, 7, 44, 36, 36, - 77, 67, 2, 2, 2, 2, 2, 2, 2, 97, 97, 67, 43, 67, 67, 67, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 86, - 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, - 57, 43, 43, 43, 43, 43, 43, 85, 43, 43, 60, 43, 36, 36, 70, 43, - 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, - 67, 67, 67, 76, 67, 67, 92, 67, 2, 2, 97, 67, 21, 64, 44, 44, - 36, 36, 36, 36, 36, 94, 87, 43, 85, 43, 43, 43, 87, 85, 87, 71, - 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 86, 43, 36, 36, 43, - 71, 86, 98, 94, 86, 86, 86, 36, 70, 43, 71, 36, 36, 36, 36, 36, - 36, 85, 87, 85, 86, 86, 87, 94, 7, 7, 7, 7, 7, 86, 87, 67, - 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, - 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, - 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, - 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, - 2, 2, 2, 2, 99, 27, 27, 27, 27, 27, 27, 27, 27, 27,100, 44, - 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, - 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, - 102, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,103,104, 44, - 36, 36, 36, 36, 36, 63, 2,105,106, 36, 36, 36, 61, 44, 44, 44, - 36, 43, 85, 44, 44, 44, 44, 62, 36, 43,107, 64, 44, 44, 44, 44, - 36, 43, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 61, 36, - 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 87, 43, 43, 43, 86, - 86, 86, 86, 85, 87, 43, 43, 43, 43, 43, 2, 88, 2, 66, 70, 44, - 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, - 2, 2, 2,108, 2, 59, 43, 84, 36, 83, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, - 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 61, 43, 85, 86, 87, 85, 86, 44, 44, - 86, 85, 86, 86, 87, 43, 44, 44, 92, 44, 2, 7, 7, 7, 7, 7, - 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, - 7, 7, 7, 7, 7,100, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 36, 36, 36, 70, 85, 87, 44, 2, 36, 36, 94, 85, 43, 43, 43, 80, - 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, - 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, - 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, - 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 2, - 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 2, - 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, - 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, - 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, - 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 16, 16, 16, 16, 34,110, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, - 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, - 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, - 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, - 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,111, 40, 40, - 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, - 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, - 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16,112,112,112,112, - 16, 16,110, 16, 11, 11,113,114, 41, 16,110, 16, 11, 11,113, 41, - 16, 16, 44, 16, 11, 11,115, 41, 16, 16, 16, 16, 11, 11,116, 41, - 44, 16,110, 16, 11, 11,113,117,118,118,118,118,118,119, 65, 65, - 120,120,120, 2,121,122,121,122, 2, 2, 2, 2,123, 65, 65,124, - 2, 2, 2, 2,125,126, 2,127,128, 2,129,130, 2, 2, 2, 2, - 2, 9,128, 2, 2, 2, 2,131, 65, 65,132, 65, 65, 65, 65, 65, - 133, 44, 27, 27, 27, 8,129,134, 27, 27, 27, 27, 27, 8,129,104, - 40, 40, 40, 40, 40, 40, 81, 44, 20, 20, 20, 20, 20, 20, 20, 20, - 135, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43,136, 51, - 109, 51,109, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, - 67,137, 67,138, 67, 34, 11, 16, 11, 32,138, 67, 49, 11, 11, 67, - 67, 67,137,137,137, 11, 11,139, 11, 11, 35, 36, 39, 67, 16, 11, - 8, 8, 49, 16, 16, 26, 67,140, 27, 27, 27, 27, 27, 27, 27, 27, - 105,105,105,105,105,105,105,105,105,141,142,105,143, 67, 44, 44, - 8, 8,144, 67, 67, 8, 67, 67,144, 26, 67,144, 67, 67, 67,144, - 67, 67, 67, 67, 67, 67, 67, 8, 67,144,144, 67, 67, 67, 67, 67, - 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, - 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, - 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, - 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, - 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, - 67, 67, 67, 26, 67, 67, 67, 67, 26, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 26, - 67, 67, 67, 67, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, - 27, 27, 67, 67, 67, 67, 67, 67, 8, 8,129,147, 8, 8, 8, 8, - 8, 8, 8, 4, 4, 4, 4, 4, 8,129,148,148,148,148,148,148, - 148,148,148,148,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, - 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,144, 26, 8, 8,144, 67, - 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, - 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, - 32, 32,140, 67, 67,138, 34,149, 43, 32, 44, 44, 93, 2, 99, 2, - 16, 16, 16,150, 44, 44,150, 44, 36, 36, 36, 36, 44, 44, 44, 52, - 64, 44, 44, 44, 44, 44, 44, 57, 36, 36, 36, 61, 44, 44, 44, 44, - 36, 36, 36, 61, 36, 36, 36, 61, 2,121,121, 2,125,126,121, 2, - 2, 2, 2, 6, 2,108,121, 2,121, 4, 4, 4, 4, 2, 2, 88, - 2, 2, 2, 2, 2,120, 2, 2,108,151, 2, 2, 2, 2, 2, 2, - 67, 2,152,148,148,148,153, 44, 67, 67, 67, 67, 67, 55, 67, 67, - 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, - 1, 2,154,155, 4, 4, 4, 4, 4, 67, 4, 4, 4, 4,156,157, - 158,105,105,105,105, 43, 43, 86,159, 40, 40, 67,105,160, 63, 67, - 36, 36, 36, 61, 57,161,162, 69, 36, 36, 36, 36, 36, 63, 40, 69, - 44, 44, 62, 36, 36, 36, 36, 36, 67, 27, 27, 67, 67, 67, 67, 67, - 67, 67, 67, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, 92, - 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, - 163, 27, 27, 27, 27, 27, 27, 27, 36, 36, 83, 36, 36, 36, 36, 36, - 67, 67, 67, 92, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36,164, 2, - 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, - 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, - 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, - 41, 41, 41,162, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, + 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, + 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 43, 44, 16, 10, + 43, 43, 40, 45, 11, 46, 46, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 47, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 48, 34, 32, 34, 11, + 32, 49, 42, 42, 50, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 47, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 46, 51, 2, 2, 2, + 16, 16, 16, 16, 52, 53, 54, 55, 56, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 57, 58, 59, 42, 58, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 61, + 36, 62, 63, 43, 43, 43, 43, 43, 64, 64, 64, 8, 9, 65, 2, 66, + 42, 42, 42, 42, 42, 59, 67, 2, 68, 36, 36, 36, 36, 69, 42, 42, + 7, 7, 7, 7, 7, 2, 2, 36, 70, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 71, 42, 42, 42, 72, 49, 42, 42, 73, 74, 75, 42, 42, 36, + 7, 7, 7, 7, 7, 36, 76, 77, 2, 2, 2, 2, 2, 2, 2, 78, + 69, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 79, 61, 36, + 36, 36, 36, 42, 42, 42, 42, 42, 70, 43, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, + 42, 42, 39, 21, 2, 80, 56, 20, 36, 36, 36, 42, 42, 74, 42, 42, + 42, 42, 74, 42, 74, 42, 42, 43, 2, 2, 2, 2, 2, 2, 2, 63, + 36, 36, 36, 36, 69, 42, 43, 63, 36, 36, 36, 36, 36, 60, 43, 43, + 36, 36, 36, 36, 81, 36, 36, 36, 64, 43, 43, 56, 42, 42, 42, 42, + 36, 36, 36, 36, 82, 42, 42, 42, 42, 83, 42, 42, 42, 42, 42, 42, + 42, 84, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 84, 70, 85, + 86, 42, 42, 42, 84, 85, 86, 85, 69, 42, 42, 42, 36, 36, 36, 36, + 36, 42, 2, 7, 7, 7, 7, 7, 87, 36, 36, 36, 36, 36, 36, 36, + 69, 85, 61, 36, 36, 36, 60, 61, 60, 61, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 60, 36, 36, 36, 60, 60, 43, 36, 36, 43, 70, 85, + 86, 42, 79, 88, 89, 88, 86, 60, 43, 43, 43, 88, 43, 43, 36, 61, + 36, 42, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 55, 62, 79, + 56, 84, 61, 36, 36, 60, 43, 61, 60, 36, 61, 60, 36, 43, 79, 85, + 86, 79, 43, 56, 79, 56, 42, 43, 56, 43, 43, 43, 61, 36, 60, 60, + 43, 43, 43, 7, 7, 7, 7, 7, 42, 36, 69, 63, 43, 43, 43, 43, + 56, 84, 61, 36, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, + 60, 36, 61, 36, 36, 43, 70, 85, 86, 42, 42, 56, 84, 88, 86, 43, + 60, 43, 43, 43, 43, 43, 43, 43, 65, 43, 43, 43, 61, 42, 42, 42, + 56, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 43, 70, 86, + 86, 42, 79, 88, 89, 88, 86, 43, 43, 43, 56, 84, 43, 43, 36, 61, + 77, 27, 27, 27, 43, 43, 43, 43, 43, 70, 61, 36, 36, 60, 43, 36, + 60, 36, 36, 43, 61, 60, 60, 36, 43, 61, 60, 43, 36, 60, 43, 36, + 36, 36, 36, 36, 36, 43, 43, 85, 84, 89, 43, 85, 89, 85, 86, 43, + 60, 43, 43, 88, 43, 43, 43, 43, 27, 90, 66, 66, 55, 91, 43, 43, + 84, 85, 70, 36, 36, 36, 60, 36, 60, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 43, 70, 42, 84, 85, 89, 42, 79, 42, 42, 43, + 43, 43, 56, 79, 36, 60, 36, 43, 43, 43, 43, 92, 27, 27, 27, 90, + 69, 85, 71, 36, 36, 36, 60, 36, 36, 36, 61, 36, 36, 43, 70, 86, + 85, 85, 89, 84, 89, 85, 42, 43, 43, 43, 88, 89, 43, 43, 36, 60, + 61, 93, 43, 43, 43, 43, 43, 43, 42, 85, 36, 36, 36, 36, 60, 36, + 36, 36, 36, 36, 36, 69, 70, 85, 86, 42, 79, 85, 89, 85, 86, 76, + 43, 43, 36, 93, 27, 27, 27, 94, 27, 27, 27, 27, 90, 36, 36, 36, + 56, 85, 61, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 36, 36, 36, + 36, 61, 36, 36, 36, 36, 61, 43, 36, 36, 36, 60, 43, 79, 43, 88, + 85, 42, 79, 79, 85, 85, 85, 85, 43, 85, 63, 43, 43, 43, 43, 43, + 61, 36, 36, 36, 36, 36, 36, 36, 69, 36, 42, 42, 42, 79, 43, 95, + 36, 36, 36, 74, 42, 42, 42, 59, 7, 7, 7, 7, 7, 2, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 61, 60, 60, 36, 36, 60, 36, 36, + 36, 36, 61, 61, 36, 36, 36, 36, 69, 36, 42, 42, 42, 42, 70, 43, + 36, 36, 60, 80, 42, 42, 42, 79, 7, 7, 7, 7, 7, 43, 36, 36, + 76, 66, 2, 2, 2, 2, 2, 2, 2, 96, 96, 66, 42, 66, 66, 66, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 49, 49, 49, 4, 4, 85, + 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, + 56, 42, 42, 42, 42, 42, 42, 84, 42, 42, 59, 42, 36, 36, 69, 42, + 42, 42, 42, 42, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 79, 66, + 66, 66, 66, 75, 66, 66, 91, 66, 2, 2, 96, 66, 21, 63, 43, 43, + 36, 36, 36, 36, 36, 93, 86, 42, 84, 42, 42, 42, 86, 84, 86, 70, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 85, 42, 36, 36, 42, + 70, 85, 97, 93, 85, 85, 85, 36, 69, 42, 70, 36, 36, 36, 36, 36, + 36, 84, 86, 84, 85, 85, 86, 93, 7, 7, 7, 7, 7, 85, 86, 66, + 11, 11, 11, 47, 43, 43, 47, 43, 16, 16, 16, 16, 16, 52, 44, 16, + 36, 36, 36, 36, 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, + 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, 36, 36, 36, 36, + 36, 36, 36, 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 56, 42, + 2, 2, 2, 2, 98, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, + 66, 66, 66, 66, 66, 43, 43, 43, 11, 11, 11, 43, 16, 16, 16, 43, + 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 71, + 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,102,103, 43, + 36, 36, 36, 36, 36, 62, 2,104,105, 36, 36, 36, 60, 43, 43, 43, + 36, 42, 84, 43, 43, 43, 43, 61, 36, 42,106, 63, 43, 43, 43, 43, + 36, 42, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 60, 36, + 60, 42, 43, 43, 43, 43, 43, 43, 36, 36, 42, 86, 42, 42, 42, 85, + 85, 85, 85, 84, 86, 42, 42, 42, 42, 42, 2, 87, 2, 65, 69, 43, + 7, 7, 7, 7, 7, 43, 43, 43, 27, 27, 27, 27, 27, 43, 43, 43, + 2, 2, 2,107, 2, 58, 42, 83, 36, 82, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 60, 43, 43, 43, 36, 36, 69, 70, 36, 36, 36, 36, + 36, 36, 36, 36, 69, 60, 43, 43, 36, 36, 36, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 60, 42, 84, 85, 86, 84, 85, 43, 43, + 85, 84, 85, 85, 86, 42, 43, 43, 91, 43, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 43, 36, 36, 60, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 43, 43, 36, 36, 36, 36, 36, 43, 43, 43, + 7, 7, 7, 7, 7, 99, 43, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 36, 36, 36, 69, 84, 86, 43, 2, 36, 36, 93, 84, 42, 42, 42, 79, + 84, 84, 86, 42, 42, 42, 84, 85, 85, 86, 42, 42, 42, 42, 79, 56, + 2, 2, 2, 87, 2, 2, 2, 43, 42, 42, 42, 42, 42, 42, 42,108, + 42, 42, 42, 42, 42, 42, 42, 43, 42, 42, 42, 42, 42, 42, 43, 43, + 42, 42, 97, 36, 36, 36, 36, 36, 36, 36, 84, 42, 42, 84, 84, 85, + 85, 84, 97, 36, 36, 36, 60, 2, 96, 66, 66, 66, 66, 49, 42, 42, + 42, 42, 66, 66, 66, 66, 21, 2, 42, 97, 36, 36, 36, 36, 36, 36, + 93, 42, 42, 85, 42, 86, 42, 36, 36, 36, 36, 84, 42, 85, 86, 86, + 42, 85, 43, 43, 43, 43, 2, 2, 36, 36, 85, 85, 85, 85, 42, 42, + 42, 42, 85, 42, 43, 92, 2, 2, 7, 7, 7, 7, 7, 43, 61, 36, + 36, 36, 36, 36, 39, 39, 39, 2, 16, 16, 16, 16, 34,109, 43, 43, + 11, 11, 11, 11, 11, 46, 47, 11, 2, 2, 2, 2, 43, 43, 43, 43, + 42, 59, 42, 42, 42, 42, 42, 42, 84, 42, 42, 42, 70, 36, 69, 36, + 36, 36, 70, 93, 42, 60, 43, 43, 16, 16, 16, 16, 16, 16, 39, 39, + 39, 39, 39, 39, 39, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, + 16, 16, 16, 16, 16,110, 39, 39, 32, 32, 32, 16, 16, 16, 16, 32, + 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 43, 11, 11, 11, 43, + 16, 16, 16, 16, 47, 47, 47, 47, 16, 16, 16, 16, 16, 16, 16, 43, + 16, 16, 16, 16,111,111,111,111, 16, 16,109, 16, 11, 11,112,113, + 40, 16,109, 16, 11, 11,112, 40, 16, 16, 43, 16, 11, 11,114, 40, + 16, 16, 16, 16, 11, 11,115, 40, 43, 16,109, 16, 11, 11,112,116, + 117,117,117,117,117,118, 64, 64,119,119,119, 2,120,121,120,121, + 2, 2, 2, 2,122, 64, 64,123, 2, 2, 2, 2,124,125, 2,126, + 127, 2,128,129, 2, 2, 2, 2, 2, 9,127, 2, 2, 2, 2,130, + 64, 64,131, 64, 64, 64, 64, 64,132, 43, 27, 27, 27, 8,128,133, + 27, 27, 27, 27, 27, 8,128,103, 39, 39, 39, 39, 39, 39, 80, 43, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43, 43, + 42, 42, 42, 42, 42, 42,134, 50,108, 50,108, 42, 42, 42, 42, 42, + 79, 43, 43, 43, 43, 43, 43, 43, 66,135, 66,136, 66, 34, 11, 16, + 11, 32,136, 66, 48, 11, 11, 66, 66, 66,135,135,135, 11, 11,137, + 11, 11, 35, 36,138, 66, 16, 11, 8, 8, 48, 16, 16, 26, 66,139, + 27, 27, 27, 27, 27, 27, 27, 27,104,104,104,104,104,104,104,104, + 104,140,141,104,142, 66, 43, 43, 8, 8,143, 66, 66, 8, 66, 66, + 143, 26, 66,143, 66, 66, 66,143, 66, 66, 66, 66, 66, 66, 66, 8, + 66,143,143, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 66, 66, 66, 66, 4, 4, 66, 66, + 8, 66, 66, 66,144,145, 66, 66, 66, 66, 66, 66, 66, 66,143, 66, + 66, 66, 66, 66, 66, 26, 8, 8, 8, 8, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 8, 8, 8, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 91, 43, 43, 27, 27, 27, 27, 27, 27, 66, 66, + 66, 66, 66, 66, 66, 27, 27, 27, 66, 66, 66, 26, 66, 66, 66, 66, + 26, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, + 66, 66, 66, 66, 66, 66, 66, 26, 66, 66, 66, 66, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, + 8, 8,128,146, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,128,147,147,147,147,147,147,147,147,147,147,146, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,143, 26, 8, 8,143, 66, 66, 66, 43, 66, 66, 66, 66, 66, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 39, 11, + 32, 32,139, 66, 66,136, 34,148, 42, 32, 43, 43, 92, 2, 98, 2, + 16, 16, 16,149, 43, 43,149, 43, 36, 36, 36, 36, 43, 43, 43, 51, + 63, 43, 43, 43, 43, 43, 43, 56, 36, 36, 36, 60, 43, 43, 43, 43, + 36, 36, 36, 60, 36, 36, 36, 60, 2,120,120, 2,124,125,120, 2, + 2, 2, 2, 6, 2,107,120, 2,120, 4, 4, 4, 4, 2, 2, 87, + 2, 2, 2, 2, 2,119, 2, 2,107,150, 2, 2, 2, 2, 2, 2, + 66, 2,151,147,147,147,152, 43, 66, 66, 66, 66, 66, 54, 66, 66, + 66, 66, 43, 43, 43, 43, 43, 43, 66, 66, 66, 43, 43, 43, 43, 43, + 1, 2,153,154, 4, 4, 4, 4, 4, 66, 4, 4, 4, 4,155,156, + 157,104,104,104,104, 42, 42, 85,158, 39, 39, 66,104,159, 62, 66, + 36, 36, 36, 60, 56,160,161, 68, 36, 36, 36, 36, 36, 62, 39, 68, + 43, 43, 61, 36, 36, 36, 36, 36, 66, 27, 27, 66, 66, 66, 66, 66, + 66, 66, 66, 43, 43, 43, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, + 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, + 162, 27, 27, 27, 27, 27, 27, 27, 36, 36, 82, 36, 36, 36, 36, 36, + 66, 66, 66, 91, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 2, + 7, 7, 7, 7, 7, 36, 43, 43, 32, 32, 32, 32, 32, 32, 32, 69, + 50,164, 42, 42, 42, 42, 42, 87, 32, 32, 32, 32, 32, 32, 39, 42, + 36, 36, 36,104,104,104,104,104, 42, 2, 2, 2, 43, 43, 43, 43, + 40, 40, 40,161, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 41,165, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 44, 32,150,150, 32, 32, 32, 47, 44, - 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, - 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, - 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 86, 86, 86, 86, 86, 86, 86, 86, 43, 44, 44, 44, 44, 2, - 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, - 36, 36, 36, 70, 43, 43, 43, 43, 43, 86, 44, 44, 44, 44, 44, 93, - 36, 70, 86, 43, 43, 86, 43, 86,107, 2, 2, 2, 2, 2, 2, 52, - 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, - 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 85, - 87, 85, 87, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 85, 44, - 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 94, 85, 36, - 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 62, 83, 2, 36, 36, 36, 36, 36, 94, 43, 86, - 2, 83,169, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, - 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,114, 40, 40, - 16, 16, 16, 16,111, 41, 44, 44, 36, 94, 87, 86, 85,107, 87, 44, - 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, - 170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171, - 16, 16, 16,110, 44, 44, 44, 44, 44,150, 16, 16, 44, 44, 62, 71, - 36, 36, 36, 36,172, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, - 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, - 41,117, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36,148, 44, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 44, 55, 36, 36, 36, 36, 36, 36,168, 67, - 2, 2, 2,152,130, 44, 44, 44, 6,173,174,148,148,148,148,148, - 148,148,130,152,130, 2,127,175, 2, 64, 2, 2,156,148,148,130, - 2,176, 8,177, 66, 2, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 61, 79, 93, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,129,130, 4, 2, 36, 36, 36, 36, 36, - 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, - 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, - 20,178, 56,135, 26, 8,144, 92, 44, 44, 44, 44, 79, 65, 67, 44, - 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, - 2, 64, 44,179, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, - 105,105,143, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 92, - 67, 67, 67, 67, 67, 67, 92, 44, 92, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 67, 50, 44,180, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, - 155, 36, 36, 36, 36,181, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 93, 36, 36, 44, 44, 36, 36, 36, 36, - 182,105,105, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, - 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, - 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, 44, 93, - 11, 11, 11, 11, 11, 47, 11, 11, 11, 47, 11,150, 16, 16, 16, 16, - 16,150, 16, 16, 16, 16, 16, 16, 16,150, 16, 16, 16,150,110, 44, - 40, 40, 40, 52, 40, 40, 40, 40, 81, 40, 40, 40, 40, 81, 44, 44, - 36, 36, 36, 44, 61, 36, 36, 36, 36, 36, 36, 62, 61, 44, 61, 62, - 36, 36, 36, 93, 27, 27, 27, 27, 36, 36, 36, 77,163, 27, 27, 27, - 44, 44, 44,179, 27, 27, 27, 27, 36, 61, 36, 44, 44,179, 27, 27, - 36, 36, 36, 27, 27, 27, 44, 93, 36, 36, 36, 36, 36, 44, 44, 93, - 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, - 70, 43, 57, 80, 44, 44, 43, 43, 36, 36, 62, 36, 62, 36, 36, 36, - 36, 36, 36, 44, 43, 80, 44, 57, 27, 27, 27, 27,100, 44, 44, 44, - 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36,183, 30, - 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 78, 36, 36, 36, - 36, 36, 70, 80, 44,179, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, - 36, 36, 36, 44, 93, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, - 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, - 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, - 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, - 7, 7, 7, 7, 7, 36, 36, 69, 11, 11, 11, 44, 57, 43, 43,159, - 16, 16, 16, 44, 44, 44, 44, 8, 27, 27, 27, 27, 27, 27, 27,100, - 36, 36, 36, 36, 36, 57,184, 44, 36, 44, 44, 44, 44, 44, 44, 44, - 44, 36, 61, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, - 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, - 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, - 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, - 43, 43, 43, 60, 2, 2, 2, 44, 27, 27, 27, 7, 7, 7, 7, 7, - 71, 70, 71, 44, 44, 44, 44, 57, 86, 87, 43, 85, 87, 60,185, 2, - 2, 80, 44, 44, 44, 44, 79, 44, 43, 71, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 70, 43, 43, 87, 43, 43, 43, 80, 7, 7, 7, 7, 7, - 2, 2, 94, 98, 44, 44, 44, 44, 36, 70, 2, 61, 44, 44, 44, 44, - 36, 94, 86, 43, 43, 43, 43, 85, 98, 36, 63, 2, 59, 43, 60, 87, - 7, 7, 7, 7, 7, 63, 63, 2,179, 27, 27, 27, 27, 27, 27, 27, - 27, 27,100, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 86, 87, - 43, 86, 85, 43, 2, 2, 2, 71, 70, 44, 44, 44, 44, 44, 44, 44, - 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, - 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, - 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, - 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, - 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, - 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 62, 44, 61, - 36, 36, 36, 62, 86, 87, 43, 43, 80, 90, 89, 89, 86, 90, 86, 85, - 71, 71, 2, 93, 64, 44, 44, 44, 57, 80, 44, 44, 44, 44, 44, 44, - 36, 36, 94, 86, 43, 43, 43, 43, 86, 43, 85, 71, 36, 63, 2, 2, - 7, 7, 7, 7, 7, 2, 93, 71, 86, 87, 43, 43, 85, 85, 86, 87, - 85, 43, 36, 72, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 94, - 86, 43, 43, 44, 86, 86, 43, 87, 60, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 43, 44, 86, 87, 43, 43, 43, 85, 87, 87, - 60, 2, 61, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 2, 64, 44, - 36, 36, 36, 36, 36, 70, 87, 86, 43, 43, 43, 87, 63, 44, 44, 44, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 61, 57, 87, 86, 43, 43, 87, 43, 43, 44, 44, - 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, - 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, - 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, - 98, 87, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, - 94, 86, 43, 43, 44, 43, 86, 86, 71, 72, 90, 44, 44, 44, 44, 44, - 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 85, 70, 43, 60, - 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, - 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, - 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, - 63, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 87, - 63, 2, 2, 44, 44, 44, 44, 44, 2, 36, 36, 36, 36, 36, 36, 36, - 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 89, 43, 43, 43, - 85, 43, 87, 80, 44, 44, 44, 44, 36, 36, 36, 61, 36, 62, 36, 36, - 70, 43, 43, 80, 44, 80, 43, 57, 43, 43, 43, 70, 44, 44, 44, 44, - 36, 36, 36, 62, 61, 36, 36, 36, 36, 36, 36, 36, 36, 86, 86, 90, - 43, 89, 87, 87, 61, 44, 44, 44, 36, 70, 85,107, 64, 44, 44, 44, - 43, 94, 36, 36, 36, 36, 36, 36, 36, 36, 86, 43, 43, 80, 44, 86, - 85, 60, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 80, 44, 44, - 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, - 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, - 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, - 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, - 43, 43, 43, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 43, - 43, 43, 43, 43, 43, 86, 87, 43, 43, 43, 60, 44, 44, 44, 44, 44, - 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, - 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 62, 36, 40, 69, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 83,164, 2, 27, 27, 27, 30, 2, 64, 44, 44, - 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, - 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, - 86, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 62, - 40, 40, 52, 40, 40, 40, 52, 81, 36, 61, 44, 44, 44, 44, 44, 44, - 44, 61, 44, 44, 44, 44, 44, 44, 36, 61, 62, 44, 44, 44, 44, 44, - 44, 44, 36, 36, 44, 44, 44, 44, 36, 36, 36, 36, 36, 44, 50, 60, - 65, 65, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 44, - 43, 43, 43, 80, 44, 44, 44, 44, 67, 67, 67, 92, 55, 67, 67, 67, - 67, 67,186, 87, 43, 67,186, 86, 86,187, 65, 65, 65, 84, 43, 43, - 43, 76, 50, 43, 43, 43, 67, 67, 67, 67, 67, 67, 67, 43, 43, 67, - 67, 43, 76, 44, 44, 44, 44, 44, 27, 27, 44, 44, 44, 44, 44, 44, - 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 16, 16, 16,110, 16, 16, 16, 16, 16, - 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 11, - 44, 47, 48, 47, 48, 11, 47, 11, 11, 11, 11, 16, 16,150,150, 16, - 16, 16,150, 16, 16, 16, 16, 16, 16, 16, 11, 48, 11, 47, 48, 11, - 11, 11, 47, 11, 11, 11, 47, 16, 16, 16, 16, 16, 11, 48, 11, 47, - 11, 11, 47, 47, 44, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, - 16, 16, 16, 44, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, - 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, - 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, - 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, - 16, 33, 16, 16, 16, 32, 44, 7, 43, 43, 43, 76, 67, 50, 43, 43, - 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, - 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, - 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, - 44, 44,150, 16, 16,110, 44, 44, 43, 43, 43, 80, 43, 43, 43, 43, - 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, - 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 57, - 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, - 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, - 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, - 188, 7, 7, 7, 7,189, 44, 93, 36, 36, 36, 61, 36, 36, 62, 61, - 36, 36, 61,179, 27, 27, 27, 27, 16, 16, 43, 43, 43, 74, 44, 44, - 27, 27, 27, 27, 27, 27,163, 27,190, 27,100, 44, 44, 44, 44, 44, - 27, 27, 27, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, 44, - 36, 36, 62, 36, 36, 36, 36, 36, 62, 61, 61, 62, 62, 36, 36, 36, - 36, 61, 36, 36, 62, 62, 44, 44, 44, 61, 44, 62, 62, 62, 62, 36, - 62, 61, 61, 62, 62, 62, 62, 62, 62, 61, 61, 62, 36, 61, 36, 36, - 36, 61, 36, 36, 62, 36, 61, 61, 36, 36, 36, 36, 36, 62, 36, 36, - 62, 36, 62, 36, 36, 62, 36, 36, 8, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, - 27, 27, 27, 27, 27, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 44, - 44, 44, 44, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 92, 44, 44, 44, 67, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 25, 41, 41, 67, 67, 67, 67, 44, 44, 67, 67, - 67, 67, 67, 92, 44, 55, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 92, 44, 44, 44, 67, - 67, 67, 67, 67, 67, 67, 92, 55, 67, 92, 67, 67, 67, 67, 67, 67, - 79, 44, 44, 44, 44, 44, 44, 44,171,171,171,171,171,171,171, 44, - 171,171,171,171,171,171,171, 0, 0, 0, 29, 21, 21, 21, 23, 21, - 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, - 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, - 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, - 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, - 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, - 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, 25, 9, 26, 12, 11, 11, - 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, 2, 12, 17, 12, 21, 12, - 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, 1, 1, 21, 23, 26, 26, - 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, - 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, 6, 2, 24, 7, 7, 6, - 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 2, 10, 10, 2, 15, 26, - 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, 21, 26, 10, 7, 21, 15, - 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, 14, 14, 14, 7, 10, 21, - 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, 8, 24, 5, 24, 2, 24, - 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, 20, 19, 22, 20, 27, 28, - 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, - 1, 2, 15, 6, 18, 6, 23, 2, 12, 11, 9, 26, 26, 9, 26, 5, - 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, - 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, - 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, - 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, - 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 25, 2, 25, 24, 2, 15, - 12, 15, 14, 2, 21, 14, 7, 15, 12, 17, 21, 1, 26, 10, 10, 1, - 7, 13, 13, 2, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, + 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 46, 43, + 51, 39,166, 35, 39, 35, 36, 36, 36, 70, 36, 70, 36, 69, 36, 36, + 36, 93, 86, 84, 66, 66, 79, 43, 27, 27, 27, 66,167, 43, 43, 43, + 36, 36, 2, 2, 43, 43, 43, 43, 85, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 85, 85, 85, 85, 85, 85, 85, 85, 42, 43, 43, 43, 43, 2, + 42, 36, 36, 36, 2, 71, 71, 69, 36, 36, 36, 42, 42, 42, 42, 2, + 36, 36, 36, 69, 42, 42, 42, 42, 42, 85, 43, 43, 43, 43, 43, 92, + 36, 69, 85, 42, 42, 85, 42, 85,106, 2, 2, 2, 2, 2, 2, 51, + 7, 7, 7, 7, 7, 43, 43, 2, 36, 36, 69, 68, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 60, 36, 36, 36, 36, 69, 42, 42, 84, + 86, 84, 86, 79, 43, 43, 43, 43, 36, 69, 36, 36, 36, 36, 84, 43, + 7, 7, 7, 7, 7, 43, 2, 2, 68, 36, 36, 76, 66, 93, 84, 36, + 70, 42, 70, 69, 70, 36, 36, 42, 69, 60, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 61, 82, 2, 36, 36, 36, 36, 36, 93, 42, 85, + 2, 82,168, 79, 43, 43, 43, 43, 61, 36, 36, 60, 61, 36, 36, 60, + 61, 36, 36, 60, 43, 43, 43, 43, 16, 16, 16, 16, 16,113, 39, 39, + 16, 16, 16, 16,110, 40, 43, 43, 36, 93, 86, 85, 84,106, 86, 43, + 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 43, 61, 36, 36, + 169,169,169,169,169,169,169,169,170,170,170,170,170,170,170,170, + 16, 16, 16,109, 43, 43, 43, 43, 43,149, 16, 16, 43, 43, 61, 70, + 36, 36, 36, 36,171, 36, 36, 36, 36, 36, 36, 60, 36, 36, 60, 60, + 36, 61, 60, 36, 36, 36, 36, 36, 36, 40, 40, 40, 40, 40, 40, 40, + 40, 22, 66, 66, 66, 66, 66, 66, 66, 77, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,147, 66, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 66, 66, 66, 66, 36, 36, 36, 36, 36, 36,167, 66, + 2, 2, 2,151,129, 43, 43, 43, 6,172,173,147,147,147,147,147, + 147,147,129,151,129, 2,126,174, 2, 63, 2, 2,155,147,147,129, + 2,175, 8,176, 65, 2, 43, 43, 36, 36, 60, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 60, 78, 92, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,128,129, 4, 2, 36, 36, 36, 36, 36, + 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, + 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 60, 43, + 20,177, 55,178, 26, 8,143, 91, 43, 43, 43, 43, 78, 64, 66, 43, + 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 60, 36, 61, + 2, 63, 43,179, 27, 27, 27, 27, 27, 27, 43, 54, 66, 66, 66, 66, + 104,104,142, 27, 90, 66, 66, 66, 66, 66, 66, 66, 66, 27, 66, 91, + 66, 66, 66, 66, 66, 66, 91, 43, 91, 43, 43, 43, 43, 43, 43, 43, + 66, 66, 66, 66, 66, 66, 49, 43,180, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 43, 43, 27, 27, 43, 43, 43, 43, 61, 36, + 154, 36, 36, 36, 36,181, 43, 43, 36, 36, 36, 42, 42, 79, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 92, 36, 36, 43, 43, 36, 36, 36, 36, + 182,104,104, 43, 43, 43, 43, 43, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 43, 43, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 43, 43, + 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, 43, 92, + 11, 11, 11, 11, 11, 46, 11, 11, 11, 46, 11,149, 16, 16, 16, 16, + 16,149, 16, 16, 16, 16, 16, 16, 16,149, 16, 16, 16,149,109, 43, + 39, 39, 39, 51, 39, 39, 39, 39, 80, 39, 39, 39, 39, 80, 43, 43, + 36, 36, 36, 43, 60, 36, 36, 36, 36, 36, 36, 61, 60, 43, 60, 61, + 36, 36, 36, 92, 27, 27, 27, 27, 36, 36, 36, 76,162, 27, 27, 27, + 43, 43, 43,179, 27, 27, 27, 27, 36, 60, 36, 43, 43,179, 27, 27, + 36, 36, 36, 27, 27, 27, 43, 92, 36, 36, 36, 36, 36, 43, 43, 92, + 36, 36, 36, 36, 43, 43, 27, 36, 43, 27, 27, 27, 27, 27, 27, 27, + 69, 42, 56, 79, 43, 43, 42, 42, 36, 36, 61, 36, 61, 36, 36, 36, + 36, 36, 36, 43, 42, 79, 43, 56, 27, 27, 27, 27, 99, 43, 43, 43, + 2, 2, 2, 2, 63, 43, 43, 43, 36, 36, 36, 36, 36, 36,183, 30, + 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 77, 36, 36, 36, + 36, 36, 69, 79, 43,179, 27, 27, 2, 2, 2, 63, 43, 43, 43, 43, + 36, 36, 36, 43, 92, 2, 2, 2, 36, 36, 36, 43, 27, 27, 27, 27, + 36, 60, 43, 43, 27, 27, 27, 27, 36, 43, 43, 43, 92, 2, 63, 43, + 43, 43, 43, 43,179, 27, 27, 27, 11, 46, 43, 43, 43, 43, 43, 43, + 16,109, 43, 43, 43, 27, 27, 27, 36, 36, 42, 42, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 68, 11, 11, 11, 43, 56, 42, 42,158, + 16, 16, 16, 43, 43, 43, 43, 8, 27, 27, 27, 27, 27, 27, 27, 99, + 36, 36, 36, 36, 36, 56,184, 43, 36, 43, 43, 43, 43, 43, 43, 43, + 43, 36, 82, 36, 43, 43, 43, 43, 96, 66, 66, 66, 91, 43, 43, 43, + 43, 43, 43, 43, 43, 42, 42, 42, 27, 27, 27, 94, 43, 43, 43, 43, + 180, 27, 30, 2, 2, 43, 43, 43, 36, 42, 42, 2, 2, 43, 43, 43, + 36, 36,183, 27, 27, 27, 43, 43, 86, 97, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 42, 59, 2, 2, 2, 43, + 27, 27, 27, 7, 7, 7, 7, 7, 70, 69, 70, 43, 43, 43, 43, 56, + 85, 86, 42, 84, 86, 59,185, 2, 2, 79, 43, 43, 43, 43, 78, 43, + 42, 70, 36, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, 86, 42, + 42, 42, 79, 7, 7, 7, 7, 7, 2, 2, 93, 97, 43, 43, 43, 43, + 36, 69, 2, 60, 43, 43, 43, 43, 36, 93, 85, 42, 42, 42, 42, 84, + 97, 36, 62, 2, 58, 42, 59, 86, 7, 7, 7, 7, 7, 62, 62, 2, + 179, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 85, 86, 42, 85, 84, 42, 2, 2, 2, 70, + 69, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 60, 36, 36, 61, + 36, 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 62, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 69, 85, 86, 42, 42, 42, 79, 43, 43, + 42, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 56, 70, 85, + 84, 85, 89, 88, 89, 88, 85, 43, 60, 43, 43, 88, 43, 43, 61, 36, + 36, 85, 43, 42, 42, 42, 79, 43, 42, 42, 79, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 61, 43, 60, 36, 36, 36, 61, 85, 86, 42, 42, + 79, 89, 88, 88, 85, 89, 85, 84, 70, 70, 2, 92, 63, 43, 43, 43, + 56, 79, 43, 43, 43, 43, 43, 43, 36, 36, 93, 85, 42, 42, 42, 42, + 85, 42, 84, 70, 36, 62, 2, 2, 7, 7, 7, 7, 7, 2, 92, 70, + 85, 86, 42, 42, 84, 84, 85, 86, 84, 42, 36, 71, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 93, 85, 42, 42, 43, 85, 85, 42, 86, + 59, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 43, + 85, 86, 42, 42, 42, 84, 86, 86, 59, 2, 60, 43, 43, 43, 43, 43, + 2, 2, 2, 2, 2, 2, 63, 43, 36, 36, 36, 36, 36, 69, 86, 85, + 42, 42, 42, 86, 62, 43, 43, 43, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 56, 86, + 85, 42, 42, 86, 42, 42, 43, 43, 7, 7, 7, 7, 7, 27, 2, 96, + 42, 42, 42, 42, 86, 59, 43, 43, 27, 99, 43, 43, 43, 43, 43, 61, + 36, 36, 36, 60, 61, 43, 36, 36, 36, 36, 61, 60, 36, 36, 36, 36, + 85, 85, 85, 88, 89, 56, 84, 70, 97, 86, 2, 63, 43, 43, 43, 43, + 36, 36, 36, 36, 43, 36, 36, 36, 93, 85, 42, 42, 43, 42, 85, 85, + 70, 71, 89, 43, 43, 43, 43, 43, 69, 42, 42, 42, 42, 70, 36, 36, + 36, 69, 42, 42, 84, 69, 42, 59, 2, 2, 2, 58, 43, 43, 43, 43, + 69, 42, 42, 84, 86, 42, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, + 42, 42, 42, 84, 42, 2, 71, 2, 2, 63, 43, 43, 43, 43, 43, 43, + 2, 2, 2, 2, 2, 43, 43, 43, 84, 42, 84, 84, 43, 43, 43, 43, + 62, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 42, 42, 42, 86, + 62, 2, 2, 43, 43, 43, 43, 43, 2, 36, 36, 36, 36, 36, 36, 36, + 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 88, 42, 42, 42, + 84, 42, 86, 79, 43, 43, 43, 43, 36, 36, 36, 60, 36, 61, 36, 36, + 69, 42, 42, 79, 43, 79, 42, 56, 42, 42, 42, 69, 43, 43, 43, 43, + 36, 36, 36, 61, 60, 36, 36, 36, 36, 36, 36, 36, 36, 85, 85, 89, + 42, 88, 86, 86, 60, 43, 43, 43, 36, 36, 36, 36, 82, 36, 43, 43, + 36, 69, 84,106, 63, 43, 43, 43, 42, 93, 36, 36, 36, 36, 36, 36, + 36, 36, 85, 42, 42, 79, 43, 85, 84, 59, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 79, 43, 43, 27, 27, 90, 66, 66, 66, 55, 20, + 167, 66, 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 43, 43, 92, + 104,104,104,104,104,104,104,181, 2, 2, 63, 43, 43, 43, 43, 43, + 62, 63, 43, 43, 43, 43, 43, 43, 64, 64, 64, 64, 64, 64, 64, 64, + 70, 36, 36, 69, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 85, 86, 42, + 42, 42, 59, 43, 43, 43, 43, 43, 42, 42, 42, 59, 2, 2, 66, 66, + 39, 39, 96, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7,179, 27, 27, + 27, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 61, 36, + 39, 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82,163, 2, + 27, 27, 27, 30, 2, 63, 43, 43, 11, 11, 11, 11, 46,149, 16, 16, + 16, 16, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 43, 56, + 93, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 43, 43, 43, 56, 42, 73, 39, 39, 39, 39, 39, 39, + 39, 87, 79, 43, 43, 43, 43, 43, 85, 39,104,181, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 61, 36, 60, 43, 43, 43, 43, 43, 43, + 39, 39, 51, 39, 39, 39, 51, 80, 43, 60, 43, 43, 43, 43, 43, 43, + 36, 60, 61, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 43, 49, 59, 64, 64, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 66, 91, 43, 66, 66, 43, 43, 43, 66, 66, 66, + 176, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 43, 43, 43, 43, + 66, 66, 66, 91, 54, 66, 66, 66, 66, 66,186, 86, 42, 66,186, 85, + 85,187, 64, 64, 64, 83, 42, 42, 42, 75, 49, 42, 42, 42, 66, 66, + 66, 66, 66, 66, 66, 42, 42, 66, 66, 42, 75, 43, 43, 43, 43, 43, + 27, 27, 43, 43, 43, 43, 43, 43, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16,109, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 46, 11, 43, 46, 47, 46, 47, 11, 46, 11, + 11, 11, 11, 16, 16,149,149, 16, 16, 16,149, 16, 16, 16, 16, 16, + 16, 16, 11, 47, 11, 46, 47, 11, 11, 11, 46, 11, 11, 11, 46, 16, + 16, 16, 16, 16, 11, 47, 11, 46, 11, 11, 46, 46, 43, 11, 11, 11, + 46, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 43, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 43, 7, + 42, 42, 42, 75, 66, 49, 42, 42, 42, 42, 42, 42, 42, 42, 75, 66, + 66, 66, 49, 66, 66, 66, 66, 66, 66, 66, 75, 21, 2, 2, 43, 43, + 43, 43, 43, 43, 43, 56, 42, 42, 16, 16, 16, 16, 16,138, 16, 16, + 16, 16, 16, 16, 16, 16, 16,109, 43, 43,149, 16, 16,109, 43, 43, + 42, 42, 42, 79, 42, 42, 42, 42, 42, 42, 42, 42, 79, 56, 42, 42, + 42, 56, 79, 42, 42, 79, 43, 43, 39, 39, 39, 39, 39, 39, 39, 43, + 43, 43, 43, 43, 43, 43, 43, 56, 42, 42, 42, 73, 39, 39, 39, 43, + 7, 7, 7, 7, 7, 43, 43, 76, 36, 36, 36, 36, 36, 36, 36, 79, + 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 43, 43, 95, + 36, 36, 36, 36, 36, 82, 42, 42,188, 7, 7, 7, 7,189, 43, 92, + 36, 69, 36, 70, 36, 36, 36, 42, 36, 36, 69, 43, 43, 43, 43, 82, + 36, 36, 36, 60, 36, 36, 61, 60, 36, 36, 60,179, 27, 27, 27, 27, + 16, 16, 42, 42, 42, 73, 43, 43, 27, 27, 27, 27, 27, 27,162, 27, + 190, 27, 99, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 27, 27,162, + 27, 27, 27, 27, 27, 27, 27, 43, 36, 36, 61, 36, 36, 36, 36, 36, + 61, 60, 60, 61, 61, 36, 36, 36, 36, 60, 36, 36, 61, 61, 43, 43, + 43, 60, 43, 61, 61, 61, 61, 36, 61, 60, 60, 61, 61, 61, 61, 61, + 61, 60, 60, 61, 36, 60, 36, 36, 36, 60, 36, 36, 61, 36, 60, 60, + 36, 36, 36, 36, 36, 61, 36, 36, 61, 36, 61, 36, 36, 61, 36, 36, + 8, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 66, 43, 43, + 54, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, 27, 27, 90, 66, + 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 66, 66, 66, 66, 66, + 66, 91, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 91, 43, 43, 43, + 66, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 25, 40, 40, + 66, 66, 66, 66, 91, 43, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, + 8, 8, 8, 8,176, 43, 43, 43, 66, 66, 66, 66, 66, 91, 43, 66, + 66, 66, 66, 91, 91, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, 54, + 66, 66, 66, 66, 66, 91, 43, 54, 66, 91, 66, 66, 66, 66, 66, 66, + 7, 7, 7, 7, 7, 91, 43, 43, 78, 43, 43, 43, 43, 43, 43, 43, + 170,170,170,170,170,170,170, 43,170,170,170,170,170,170,170, 0, + 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, + 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, + 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, + 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, + 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, + 24, 24, 6, 24, 12, 12, 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, + 25, 9, 26, 12, 11, 11, 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, + 2, 12, 17, 12, 21, 12, 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, + 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, + 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, + 6, 2, 24, 7, 7, 6, 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, + 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, + 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, + 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, + 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, + 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, + 25, 22, 18, 21, 21, 29, 1, 2, 15, 6, 18, 6, 12, 11, 9, 26, + 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, + 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, + 18, 17, 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, + 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, + 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, + 25, 2, 25, 24, 23, 2, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, + 12, 17, 21, 1, 26, 10, 10, 1, 7, 13, 13, 2, 23, 15, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, + 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, + 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, + 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, + 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 8, 0, 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, + 0, 0, 0, 17, 18, 19, 20, 0, 21, 0, 22, 23, 0, 24, 25, 0, + 0, 24, 26, 27, 0, 24, 26, 0, 0, 24, 26, 0, 0, 24, 26, 0, + 0, 0, 26, 0, 0, 24, 28, 0, 0, 24, 26, 0, 0, 29, 26, 0, + 0, 0, 30, 0, 0, 31, 32, 0, 0, 33, 34, 0, 35, 36, 0, 37, + 38, 0, 39, 0, 0, 40, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 47, 0, 0, + 0, 0, 0, 0, 48, 0, 0, 49, 0, 50, 51, 52, 0, 53, 54, 55, + 0, 56, 0, 57, 0, 58, 0, 0, 0, 0, 59, 60, 0, 0, 0, 0, + 0, 0, 61, 62, 0, 0, 0, 0, 0, 0, 63, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 66, + 0, 0, 0, 67, 0, 68, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 70, 71, 0, 0, 72, 0, 0, 0, 0, + 0, 0, 0, 0, 73, 74, 0, 0, 0, 0, 54, 75, 0, 76, 77, 0, + 0, 78, 79, 0, 0, 0, 0, 0, 0, 80, 81, 82, 0, 0, 0, 0, + 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, 0, 86, 0, 0, 0, 87, + 0, 0, 0, 0, 88, 89, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 92, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 94, 0, 0, 95, 0, + 96, 0, 0, 0, 0, 0, 73, 97, 0, 98, 0, 0, 99,100, 0, 78, + 0, 0,101, 0, 0,102, 0, 0, 0, 0, 0,103, 0,104, 26,105, + 0, 0,106, 0, 0, 0,107, 0, 0, 0,108, 0, 0, 0, 0, 0, + 0, 66,109, 0, 0, 66, 0, 0, 0,110, 0, 0, 0,111, 0, 0, + 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0,112,113, 0, + 0, 0, 0, 79, 0, 44,114, 0,115, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, + 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, - 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, - 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, - 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, - 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, - 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, - 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, - 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, - 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, - 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, - 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, - 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, - 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, - 0, 0, 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, - 0,102, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104, - 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, - 0, 0, 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, - 0, 0, 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, - 0,118, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, - 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, - 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, - 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, - 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, - 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, - 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, - 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, - 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, - 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, - 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, - 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, - 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, - 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, - 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, - 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, - 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, - 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, - 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, - 0, 0, 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0, - 103, 0, 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107, - 108, 0, 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, - 33, 0,112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0, - 117, 0, 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, - 88, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, - 0,122, 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, - 0, 0, 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, - 0,128, 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0, - 134,135,136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, - 0, 0,138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, - 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, - 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, - 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, - 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, - 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, - 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, - 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, - 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, - 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, - 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, - 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, - 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, - 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, - 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, - 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, - 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, - 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, - 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, - 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, - 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, - 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, - 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, - 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, - 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, - 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, - 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, - 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, - 0, 38, 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0, - 115, 0, 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, - 0, 0, 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, - 79, 0, 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, - 0, 0, 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, - 0, 0, 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, - 48,105, 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, - 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230, - 230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220, - 220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220, - 220,230,230,230,230,240,230,220,220,220,230,230,230,220,220, 0, - 230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, - 234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230, - 230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0, - 230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, - 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, - 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, - 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230,220,230, - 220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230, - 230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, - 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230, - 230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, - 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107, - 118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, - 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, - 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, - 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, - 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220, - 230,220, 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0, + 0, 0, 0,122, 0, 0, 0, 0,123, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,124, + 125,126, 0, 0, 0, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,128,129, 0, 0,130, 0, 0, 0, 0,121, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,131, 0,132, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,133, 0, 0, 0, 0, + 0, 0, 0,134, 0, 0, 0, 0, 0, 0, 0,135, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0, 0, 0,137, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 2, 3, 4, 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, + 14, 15, 16, 17, 18, 1, 1, 1, 0, 0, 0, 0, 19, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, + 37, 0, 0, 0, 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, + 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, + 21, 0, 0, 47, 0, 0, 0, 0, 0, 38, 48, 1, 1, 49, 49, 50, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, + 0, 19, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, + 54, 21, 35, 1, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 56, + 57, 58, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 59, 0, 0, 0, 56, 0, 60, 0, 0, 0, 0, 0, 0, + 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 68, 0, 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 0, + 71, 72, 73, 74, 75, 76, 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, + 0, 80, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, + 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 83, 0, 0, 0, 0, 0, 0, 19, 84, 0, 62, 0, 0, 0, + 0, 49, 1, 85, 0, 0, 0, 0, 1, 52, 15, 86, 36, 10, 21, 1, + 1, 1, 1, 41, 1, 21, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 55, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 19, 10, + 1, 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 89, 0, 0, + 88, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, + 90, 9, 12, 4, 91, 8, 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, + 94, 1, 1, 1, 1, 1, 1, 1, 1, 95, 96, 97, 0, 0, 0, 0, + 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, 0, 0, 0, 19, + 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0,102,103, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,104, 0, 0, 0, 0, 19, 0, 1, 1, 50, 0, 0, 0, 0, + 0, 0, 0, 38, 0, 0, 0, 0, 50, 0, 0, 0, 0, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 1, 1, 1, 1, + 50, 0, 0, 0, 0, 0,105, 68, 0, 0, 0, 0, 0, 0, 0, 0, + 61, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 62, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,106,107, 58, 38, 81, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, + 0, 0, 0,108, 1, 14, 4, 12, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 47, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 38, 90, 0, 0, 0, 0,109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,110, 61, 0,111, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 50, 0, 0, 0, 0, 0, 0, 19, 58, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,112, 51, 0,112, 14, 52, + 84, 0, 0, 0,113, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 62, 0, 0, 61, 0, 0, 0, 0, 0, 0,114, 0, 90, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,114, 0, 0, 0, 0,115, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 55, 0, 38, 1, 58, + 1, 58, 0, 0, 0, 0, 0, 88, 62, 0, 0, 0, 63, 89, 0, 0, + 0, 0, 0, 59,116, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, 0, 0, 0, 61, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 78, 0, 0, 0, + 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 56, 0, 89, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 61, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 92, 0, 0, 0, 0, 0, 0, + 1, 90, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,118, 0,119,120,121,122, 0,105, 4,123, 49, 23, 0, + 0, 0, 0, 0, 0, 0, 38, 50, 0, 0, 0, 0, 38, 58, 0, 0, + 0, 0, 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 59, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,124, + 0, 0, 0, 0, 0, 0, 0,113, 0, 0, 0, 0, 19, 59, 0, 38, + 0, 81, 0, 0, 0, 0, 0, 0, 4,123, 0, 0, 0, 1,125, 0, + 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,232,220,220, + 220,220,232,216,220,220,220,220,220,202,202,220,220,220,220,202, + 202,220,220,220, 1, 1, 1, 1, 1,220,220,220,220,230,230,230, + 230,240,230,220,220,220,230,230,230,220,220, 0,230,230,230,220, + 220,220,220,230,232,220,220,230,233,234,234,233,234,234,233,230, + 0, 0, 0,230, 0,220,230,230,230,230,220,230,230,230,222,220, + 230,230,220,220,230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, + 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230, + 230,220,220,230,220,230,230,220, 35, 0, 0, 0, 0, 0,230,230, + 230, 0, 0,230,230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0, + 230,220,230,230,220,220,230,220,220,230,220,230,220,230,230, 0, + 0,220, 0, 0,230,230, 0,230, 0,230,230,230,230,230, 0, 0, + 0,220,220,220,230,220,220,220,230,230, 0,220, 27, 28, 29,230, + 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, + 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, + 0, 0, 9, 0,103,103, 9, 0,107,107,107,107,118,118, 9, 0, + 122,122,122,122,220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, + 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0, + 130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, + 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, + 0,222,230,220,220, 0, 0, 0,230, 0, 0,220,230,220, 0,220, + 230,230,230,234, 0, 0, 9, 9, 0, 0, 7, 0,230,230,230, 0, 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, @@ -1683,63 +1482,81 @@ _hb_ucd_u8[17612] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 34, 9, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 13, 13, 13, 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 16, 17, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 20, 20, 20, 20, 20, 20, - 20, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 20, 33, - 34, 35, 34, 34, 36, 37, 20, 20, 20, 20, 20, 20, 38, 20, 39, 40, - 41, 41, 41, 41, 41, 42, 43, 44, 20, 20, 20, 20, 20, 20, 20, 45, - 46, 20, 20, 47, 20, 20, 20, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 20, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, + 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 13, 13, 13, + 24, 25, 26, 26, 26, 27, 13, 13, 13, 28, 29, 30, 13, 31, 32, 33, + 34, 35, 36, 37, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 38, 7, 7, 39, 7, 40, 7, 7, + 7, 41, 13, 42, 7, 7, 43, 7, 7, 7, 44, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60, 13, 13, - 13, 61, 62, 13, 13, 13, 13, 63, 13, 13, 13, 13, 13, 13, 64, 65, - 20, 20, 66, 20, 13, 13, 13, 13, 67, 13, 13, 13, 68, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, + 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, + 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 59, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 59, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 78, 69, 69, 69, 69, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, + 81, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 69, 69, 96, 97, 98, 99, 99, 99, + 100,101,102,103,104,105,106,107,108,109, 95,110,111,112,113,114, + 115,116,117,117,118,119,120,121,122,123,124,125,126,127,128,129, + 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, + 95,146,147,148,149, 95,150,151,152,153,154,155,156,157,158,159, + 160,161, 95,162,163,164,165,165,165,165,165,165,165,166,167,165, + 168, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95,169,170,170,170,170,170,170,170,170,171,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,172,173,173, + 173,173,174, 95, 95, 95, 95, 95,175, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95,176,176,176,176,177,178,179,180, 95, 95, + 181, 95,182,183,184,185,186,186,186,186,186,186,186,186,186,186, + 186,186,186,186,186,186,186,186,186,186,186,186,187,187,187,188, + 189,190, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95,191,192,193,194,195,195,196, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,197,198, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 59,199, + 59, 59, 59,200,201,202, 59,203,204,205,206,207,208, 95,209,210, + 211, 59, 59,212, 59,213,214,214,214,214,214,215, 95, 95, 95, 95, + 95, 95, 95, 95,216, 95,217,218,219, 95, 95,220, 95, 95, 95,221, + 95,222, 95,223, 95,224,225,226,227, 95, 95, 95, 95, 95,228,229, + 230, 95,231,232, 95, 95,233,234, 59,235,236, 95, 59, 59, 59, 59, + 59, 59, 59,237, 59,238,239,240, 59, 59,241,242, 59,243, 95, 95, + 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69,244, 69, 69,245, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69,246, 69, 69, 69, 69, 69, 69, 69, 69, 69,247, 69, 69, + 69, 69,248, 95, 95, 95, 69, 69, 69, 69,249, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69,250, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,251, 95, + 95, 95, 95, 95, 95, 95,252, 95,253,254, 0, 1, 2, 2, 0, 1, + 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 19, @@ -1764,46 +1581,46 @@ _hb_ucd_u8[17612] = 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 2, 2, 95, 2, 37, 37, - 37, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, - 2, 2, 2, 2, 2, 3, 3, 3, 0, 3, 3, 3, 3, 3, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7, - 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, - 5, 5, 5, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 2, 2, 2, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 2, - 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, - 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, 11, - 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 11, 11, 11, 11, 11, 2, - 11, 11, 2, 11, 11, 2, 11, 11, 2, 2, 11, 2, 11, 11, 11, 2, - 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, - 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 2, - 10, 10, 2, 10, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, 10, 10, - 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 10, 10, - 10, 10, 2, 2, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 10, - 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, 21, - 21, 21, 21, 2, 2, 21, 21, 2, 2, 21, 21, 21, 21, 21, 21, 21, - 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 2, - 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, 21, 2, - 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, 2, - 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, 2, - 22, 22, 2, 22, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2, - 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 22, 2, 22, 22, 2, 2, - 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 2, 2, 2, 2, 22, 22, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2, - 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 2, 2, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2, - 23, 23, 23, 23, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23, 23, 2, - 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23, - 23, 2, 2, 23, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 2, 2, - 2, 2, 2, 2, 2, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 2, 16, 16, 16, 2, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 2, 16, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16, 16, 2, - 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 16, 16, + 37, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, + 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, + 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, + 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, + 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, + 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, + 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, + 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, + 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, + 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, + 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, + 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 23, 23, 2, 2, 23, 23, + 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, + 2, 2, 2, 16, 16, 2, 2, 2, 2, 2, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, @@ -1850,137 +1667,135 @@ _hb_ucd_u8[17612] = 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 1, - 1, 1, 1, 1, 1, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 2, 62, 62, 76, 76, 76, 76, 76, 76, 76, 76, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 2, 2, 2, 2, 2, 2, - 2, 2, 93, 93, 93, 93, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2, - 2, 70, 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 73, 73, - 73, 73, 73, 73, 73, 73, 6, 6, 6, 2, 2, 2, 2, 2, 8, 8, - 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 19, 19, - 19, 19, 19, 19, 9, 9, 9, 9, 9, 6, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 9, 9, 9, 9, - 9, 19, 19, 19, 19, 19, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, 9, 9, 2, 2, 2, 9, - 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, - 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 2, 2, - 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, - 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 19, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, - 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 2, 2, 2, 0, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 0, 0, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, - 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, - 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, 61, - 61, 61, 61, 61, 61, 61, 2, 2, 2, 2, 2, 2, 2, 61, 61, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 0, 0, - 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 1, 1, 1, 1, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 2, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 0, 12, 12, - 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 0, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, - 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, - 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, - 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, - 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 19, 19, - 2, 19, 2, 19, 19, 19, 2, 2, 19, 19, 19, 19, 19, 19, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 2, 2, 2, 65, 65, - 65, 65, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, - 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 74, 12, 12, - 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, - 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, - 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 2, 68, 68, 68, 68, 68, 68, 2, 2, 68, 68, - 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - 92, 2, 2, 2, 2, 2, 2, 2, 2, 92, 92, 92, 92, 92, 87, 87, - 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 30, - 30, 30, 30, 30, 30, 2, 19, 19, 19, 0, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 87, 87, - 87, 87, 87, 87, 2, 2, 87, 87, 2, 2, 2, 2, 2, 2, 12, 12, - 12, 12, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 13, 13, - 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, - 2, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 2, 14, 14, 14, 14, 14, 2, 14, 2, 14, 14, - 2, 14, 14, 2, 14, 14, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, - 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, - 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, - 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 2, 2, - 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 2, 2, 2, 2, 0, - 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, - 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 2, 2, 49, 49, - 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 41, 41, - 41, 41, 41, 41, 41, 41, 41, 41, 41, 2, 2, 2, 2, 2,118,118, - 118,118,118,118,118,118,118,118,118, 2, 2, 2, 2, 2, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 2, 53, 59, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, - 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 51, 51, - 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 2, 2, 50, 50, 2, 2, 2, 2, 2, 2,135,135, - 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,106,106, - 106,106,106,106,106,106,104,104,104,104,104,104,104,104,104,104, - 104,104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,104,161,161, - 161,161,161,161,161,161,161,161,161, 2,161,161,161,161,161,161, - 161, 2,161,161, 2,161,161,161, 2,161,161,161,161,161,161,161, - 2,161,161, 2, 2, 2,170,170,170,170,170,170,170,170,170,170, - 170,170, 2, 2, 2, 2,110,110,110,110,110,110,110,110,110,110, - 110,110,110,110,110, 2,110,110,110,110,110,110, 2, 2, 19, 19, - 19, 19, 19, 19, 2, 19, 19, 2, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 2, 2, 2, 2, 2, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, - 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, - 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,128,128, - 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, - 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, - 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 97, 97, 97, 97, 2, 2, - 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, - 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, - 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88,117,117, - 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, - 112,112,112,112,112, 2, 2, 2, 2,112,112,112,112,112, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, - 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, - 82, 2, 2, 2, 2, 2,122,122,122,122,122,122,122,122,122,122, - 2, 2, 2, 2, 2, 2, 2,122,122,122,122, 2, 2, 2, 2,122, - 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, - 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, - 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, - 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2,165,165, - 165,165,165,165,165,165,165,165,165,165,165,165, 2, 2, 2,165, - 165,165,165,165,165,165, 2, 2, 2, 2, 2, 2,165,165,156,156, + 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, + 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 6, + 6, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, + 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, + 19, 19, 19, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 1, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, + 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 56, 56, + 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, + 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, + 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, + 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, + 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, + 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, + 2, 2, 2, 2, 2, 0, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, + 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, + 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, + 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 19, 19, 19, 19, 2, 19, + 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, + 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, + 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, + 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, + 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, + 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, + 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, + 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, + 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, + 3, 3, 3, 3, 0, 0, 1, 1, 1, 1, 1, 1, 6, 6, 0, 0, + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, + 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, + 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, + 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, + 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, + 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, + 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, + 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, + 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, + 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,170,170, + 170,170,170,170,170,170,170,170,170,170, 2, 2, 2, 2,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, + 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 47, 47, + 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, + 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 2, 81,120,120,120,120,120,120,120,120,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116, 2, 2, 2, + 2, 2, 2, 2, 2,116,128,128,128,128,128,128,128,128,128,128, + 128, 2,128,128, 2, 2, 2, 2, 2,128,128,128,128,128, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72,173,173, + 173,173,173,173,173,173,173,173, 2, 2, 2, 2, 2, 2, 98, 98, + 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, + 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, 57, + 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, + 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, + 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 2, 2, 2, 2, 88, 88, + 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,112,112, + 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, + 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2,122,122, + 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, + 122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130, + 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, + 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, + 2, 2, 2, 2, 2, 2,165,165,165,165,165,165,165,165,165,165, + 165,165,165,165, 2, 2, 2,165,165,165,165,165,165,165, 2, 2, + 2, 2, 2, 2,165,165, 3, 3, 3, 3, 3, 3, 3, 2,156,156, 156,156,156,156,156,156,156,156, 2,156,156,156, 2, 2,156,156, - 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, - 2, 2, 3, 3, 3, 3,147,147,147,147,147,147,147,147,148,148, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, + 2, 2, 2, 2, 2, 2,147,147,147,147,147,147,147,147,148,148, 148,148,148,148,148,148,148,148, 2, 2, 2, 2, 2, 2,158,158, 158,158,158,158,158,158,158,158, 2, 2, 2, 2, 2, 2,153,153, 153,153,153,153,153,153,153,153,153,153, 2, 2, 2, 2,149,149, @@ -2039,46 +1854,51 @@ _hb_ucd_u8[17612] = 143,143,143,143, 2,143,143, 2,143,143,143,143,143,143,143,143, 143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143,143, - 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145, 2, - 2, 2, 2, 2, 2, 2,163,163,163,163,163,163,163,163,163, 2, - 163,163,163,163,163,163,163,163,163, 2, 2, 2,163,163,163,163, - 163, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 22, 22, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 63, 63, - 63, 63, 63, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, 63, 63, - 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 63, 63, - 63, 63, 2, 2, 2, 2,157,157,157,157,157,157,157,157,157,157, - 157, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 2, 2, 80, 80, 80, 2, 2, 2, 2, 2,127,127, - 127,127,127,127,127,127,127,127,127,127,127,127,127, 2,166,166, - 166,166,166,166,166,166,166,166, 2, 2, 2, 2, 2, 2, 79, 2, - 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, - 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,159,159, - 159,159,159,159,159,159,159,159,159,159,159,159,159, 2,159,159, - 2, 2, 2, 2, 2, 2,103,103,103,103,103,103,103,103,103,103, - 103,103,103,103, 2, 2,119,119,119,119,119,119,119,119,119,119, - 119,119,119,119, 2, 2,119,119, 2,119,119,119,119,119, 2, 2, - 2, 2, 2,119,119,119,167,167,167,167,167,167,167,167,167,167, - 2, 2, 2, 2, 2, 2,146,146,146,146,146,146,146,146,146,146, - 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 2, 2, 2, 2, 2, 2,175,175,175,175,175,175,175,175,175,175, + 175,175, 2, 2, 2, 2,175,175, 2, 2, 2, 2, 2, 2,145,145, + 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, + 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, + 163, 2, 2, 2,163,163,163,163,163, 2, 2, 2, 2, 2, 86, 2, + 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, + 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, + 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2, 80, 80, + 80, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127, + 127,127,127,127,127, 2,166,166,166,166,166,166,166,166,166,166, + 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115, + 115,115,115,115,115,115,115,115,115,115,115,115,115, 2,115,115, + 2, 2, 2, 2,115,115,159,159,159,159,159,159,159,159,159,159, + 159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103, + 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, + 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,167,167, + 167,167,167,167,167,167,167,167, 2, 2, 2, 2, 2, 2,146,146, + 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2,172,172, + 172,172,172,172,172,172,172, 2, 2,172,172,172,172,172,172,172, + 172,172, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136,139, - 13, 13,155, 2, 2, 2,136,136,136,136,136,136,136,136,155,155, - 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2, 2, 2, - 2, 2, 2, 2, 2,155,136, 2, 2, 2, 2, 2, 2, 2, 17, 17, + 13, 13,155, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 2,136,136, + 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, + 155,155,155,155, 2, 2, 2, 2, 2, 2, 2, 2, 2,155,136,136, + 136,136,136,136,136, 2,136,136,136, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139,139,139,139,139,139,139,139,139, 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, 105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, 2, - 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 1, 1, - 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,131,131, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0,131,131, 131,131,131,131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, 56, 56, 56, 56, 56, 2, 56, 2, @@ -2090,7 +1910,9 @@ _hb_ucd_u8[17612] = 160,160,160,160,160, 2,152,152,152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164,164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2,168,168,168,168,168,168,168,168,168,168, - 168, 2, 2, 2, 2,168, 30, 30, 30, 30, 2, 30, 30, 2,113,113, + 168, 2, 2, 2, 2,168,174,174,174,174,174,174,174,174,174,174, + 174,174,174,174,174, 2,174,174,174,174,174,174, 2, 2, 2, 2, + 2, 2, 2, 2,174,174, 30, 30, 30, 30, 2, 30, 30, 2,113,113, 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, 113,113,113,113,113, 2,132,132,132,132,132,132,132,132,132,132, 132,132, 2, 2, 2, 2,132,132, 2, 2, 2, 2,132,132, 3, 3, @@ -2101,8 +1923,8 @@ _hb_ucd_u8[17612] = 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, - 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, @@ -2186,8 +2008,7 @@ _hb_ucd_u8[17612] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, }; -static const uint16_t -_hb_ucd_u16[10400] = +static const uint16_t _hb_ucd_u16[10832]= { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, @@ -2210,27 +2031,29 @@ _hb_ucd_u16[10400] = 48, 48, 48, 48, 168, 169, 48, 48, 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, - 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 140, 140, 140, - 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, - 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, - 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 240, 13, 13, 13, 13, 13, 13, - 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, - 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, - 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, - 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 280, 209, 281, 209, 209, 209, 209, 282, 209, 283, 279, 284, 209, 285, 286, 209, - 209, 209, 176, 140, 287, 140, 271, 271, 271, 288, 209, 209, 209, 209, 289, 271, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292, - 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 294, 295, 271, 296, 209, 209, 297, 279, 298, 279, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 32, 216, 217, 140, + 218, 48, 48, 219, 220, 160, 221, 222, 223, 48, 224, 64, 48, 48, 225, 226, + 48, 48, 227, 228, 229, 64, 48, 230, 231, 9, 9, 232, 233, 234, 235, 236, + 11, 11, 237, 27, 27, 27, 238, 239, 11, 240, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, + 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 273, 274, 275, 276, 209, 277, 278, 209, 279, + 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 209, 282, 209, 209, 209, 209, 283, 209, 284, 280, 285, 209, 286, 287, 209, + 209, 209, 176, 140, 288, 140, 272, 272, 272, 289, 209, 209, 209, 209, 290, 272, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 291, 292, 209, 209, 293, + 209, 209, 209, 209, 209, 209, 294, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 295, 296, 272, 297, 209, 209, 298, 280, 299, 280, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 279, 279, 279, 279, 279, 279, 279, 279, 299, 300, 279, 279, 279, 301, 279, 302, - 209, 209, 209, 279, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209, + 280, 280, 280, 280, 280, 280, 280, 280, 300, 301, 280, 280, 280, 302, 280, 303, + 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 209, 209, 209, 280, 304, 209, 209, 305, 209, 209, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 306, 307, 13, 13, 13, 13, 13, 13, 308, 309, 11, 11, 310, 48, 48, 48, 311, 312, 48, 313, 314, 314, 314, 314, 32, 32, 315, 316, 317, 318, 319, 320, 140, 140, 209, 321, 209, 209, 209, 209, 209, 322, @@ -2238,226 +2061,201 @@ _hb_ucd_u16[10400] = 324, 325, 326, 327, 136, 48, 48, 48, 48, 328, 178, 48, 48, 48, 48, 329, 330, 48, 48, 136, 48, 48, 48, 48, 200, 331, 48, 48, 209, 209, 332, 48, 209, 333, 334, 209, 335, 336, 209, 209, 334, 209, 209, 336, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, 48, 337, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 229, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 230, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 339, 48, 340, 140, 13, 13, 341, 342, 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, 64, 48, 365, 48, 366, 367, 48, 151, 76, 48, 48, 368, 369, 370, 371, 372, 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, 383, 384, 314, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 48, 389, 48, 48, 206, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, - 422, 271, 271, 423, 272, 272, 272, 424, 425, 426, 427, 140, 140, 209, 209, 428, + 422, 272, 272, 423, 273, 273, 273, 424, 425, 426, 427, 140, 140, 209, 209, 428, 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 48, 48, 48, 388, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 313, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, - 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, + 48, 454, 48, 455, 48, 207, 140, 140, 48, 48, 48, 456, 272, 457, 272, 272, 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, 48, 48, 475, 192, 476, 9, 477, 11, 478, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 271, 479, 48, 48, 480, 481, 482, 140, 140, 483, - 48, 464, 484, 48, 62, 485, 140, 48, 486, 140, 140, 48, 487, 140, 48, 313, - 488, 48, 48, 489, 490, 457, 491, 492, 222, 48, 48, 493, 494, 48, 196, 192, - 495, 48, 496, 497, 498, 48, 48, 499, 222, 48, 48, 500, 501, 502, 503, 504, - 48, 97, 505, 506, 507, 140, 140, 140, 508, 509, 510, 48, 48, 511, 512, 192, - 513, 83, 84, 514, 515, 516, 517, 518, 519, 48, 48, 520, 521, 522, 523, 140, - 48, 48, 48, 524, 525, 526, 481, 140, 48, 48, 48, 527, 528, 192, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 529, 530, 531, 532, 140, 140, - 48, 48, 48, 533, 534, 192, 535, 140, 48, 48, 536, 537, 192, 538, 539, 140, - 48, 540, 541, 542, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 505, 543, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 544, - 545, 546, 48, 547, 548, 192, 140, 140, 140, 140, 549, 48, 48, 550, 551, 140, - 552, 48, 48, 553, 554, 555, 48, 48, 556, 557, 558, 48, 48, 48, 48, 196, - 559, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 560, 192, - 84, 48, 529, 561, 562, 148, 175, 563, 48, 564, 565, 566, 140, 140, 140, 140, - 567, 48, 48, 568, 569, 192, 570, 48, 571, 572, 192, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 573, - 574, 115, 48, 575, 576, 577, 140, 140, 140, 140, 140, 100, 271, 578, 579, 580, + 140, 140, 140, 140, 140, 140, 272, 479, 48, 48, 480, 481, 482, 483, 140, 484, + 48, 464, 485, 48, 62, 486, 140, 48, 487, 140, 140, 48, 488, 140, 48, 313, + 489, 48, 48, 490, 491, 457, 492, 493, 223, 48, 48, 494, 495, 48, 196, 192, + 496, 48, 497, 498, 499, 48, 48, 500, 223, 48, 48, 501, 502, 503, 504, 505, + 48, 97, 506, 507, 508, 140, 140, 140, 509, 510, 511, 48, 48, 512, 513, 192, + 514, 83, 84, 515, 516, 517, 518, 519, 520, 48, 48, 521, 522, 523, 524, 140, + 48, 48, 48, 525, 526, 527, 481, 140, 48, 48, 48, 528, 529, 192, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 530, 531, 532, 533, 140, 140, + 48, 48, 48, 534, 535, 192, 536, 140, 48, 48, 537, 538, 192, 539, 540, 140, + 48, 541, 542, 543, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 506, 544, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 545, + 546, 547, 48, 548, 549, 192, 140, 140, 140, 140, 550, 48, 48, 551, 552, 140, + 553, 48, 48, 554, 555, 556, 48, 48, 557, 558, 559, 48, 48, 48, 48, 196, + 560, 140, 140, 140, 140, 140, 561, 140, 140, 140, 140, 140, 48, 48, 562, 192, + 84, 48, 530, 563, 564, 148, 175, 565, 48, 566, 567, 568, 140, 140, 140, 140, + 569, 48, 48, 570, 571, 192, 572, 48, 573, 574, 192, 48, 48, 575, 192, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 576, + 577, 115, 48, 578, 579, 580, 140, 140, 140, 140, 140, 100, 272, 581, 582, 583, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, - 272, 272, 272, 272, 272, 272, 581, 582, 48, 48, 48, 48, 48, 48, 48, 48, + 273, 273, 273, 273, 273, 273, 584, 585, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 583, - 48, 48, 48, 584, 585, 586, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 586, + 48, 48, 48, 587, 588, 589, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 71, 48, 48, 48, 48, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 587, 588, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 589, - 48, 48, 48, 590, 591, 592, 593, 594, 48, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 595, 48, 596, 192, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 9, 9, 11, 11, 271, 597, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 598, 599, 600, 600, 601, 602, 140, 140, 140, 140, 603, 604, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 440, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 605, - 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 606, - 48, 48, 607, 608, 140, 609, 610, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 590, 591, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 592, + 48, 48, 48, 593, 594, 595, 596, 597, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 598, 48, 599, 192, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 272, 600, 9, 601, 11, 602, 140, 140, + 48, 48, 48, 48, 603, 604, 605, 605, 606, 607, 140, 140, 140, 140, 608, 609, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 610, + 48, 200, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 48, 611, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 612, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 611, 613, 140, 614, 615, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, - 48, 48, 48, 48, 48, 48, 71, 151, 196, 611, 612, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 192, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 140, - 32, 32, 613, 32, 614, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 616, 617, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 618, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 619, 209, 427, 209, 620, + 32, 32, 216, 32, 621, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, - 209, 209, 615, 209, 209, 209, 616, 617, 618, 209, 619, 209, 209, 209, 287, 140, - 209, 209, 209, 209, 620, 140, 140, 140, 140, 140, 140, 140, 271, 621, 271, 621, - 209, 209, 209, 209, 209, 338, 271, 461, 140, 140, 140, 140, 140, 140, 140, 140, - 9, 622, 11, 623, 624, 625, 241, 9, 626, 627, 628, 629, 630, 9, 622, 11, - 631, 632, 11, 633, 634, 635, 636, 9, 637, 11, 9, 622, 11, 623, 624, 11, - 241, 9, 626, 636, 9, 637, 11, 9, 622, 11, 638, 9, 639, 640, 641, 642, - 11, 643, 9, 644, 645, 646, 647, 11, 648, 9, 649, 11, 650, 538, 538, 538, - 32, 32, 32, 651, 32, 32, 652, 653, 654, 655, 45, 140, 140, 140, 140, 140, - 656, 657, 658, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 659, 660, 661, 27, 27, 27, 662, 140, 663, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 151, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 666, 140, 48, 48, 667, 668, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 669, 192, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 587, 670, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 671, 200, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 672, 614, 140, 140, - 9, 9, 626, 11, 673, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 503, 271, 271, 674, 675, 140, 140, 140, 140, - 503, 271, 676, 677, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 678, 48, 679, 680, 681, 682, 683, 684, 685, 206, 686, 206, 140, 140, 140, 687, - 209, 209, 688, 209, 209, 209, 209, 209, 209, 322, 333, 689, 689, 689, 209, 323, - 690, 209, 209, 209, 209, 209, 209, 209, 209, 209, 691, 140, 140, 140, 692, 209, - 693, 209, 209, 688, 694, 695, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 696, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 697, 426, 426, - 209, 209, 209, 209, 209, 209, 209, 698, 209, 209, 209, 209, 209, 176, 688, 427, - 688, 209, 209, 209, 699, 176, 209, 209, 699, 209, 691, 688, 695, 140, 140, 140, - 209, 209, 209, 209, 209, 322, 691, 426, 700, 209, 209, 209, 701, 702, 176, 694, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 703, 209, 209, 209, 209, 209, 192, + 209, 209, 622, 209, 209, 209, 623, 624, 625, 209, 626, 209, 209, 209, 288, 140, + 209, 209, 209, 209, 627, 140, 140, 140, 140, 140, 140, 140, 272, 628, 272, 628, + 209, 209, 209, 209, 209, 338, 272, 461, 140, 140, 140, 140, 140, 140, 140, 140, + 9, 629, 11, 630, 631, 632, 242, 9, 633, 634, 635, 636, 637, 9, 629, 11, + 638, 639, 11, 640, 641, 642, 643, 9, 644, 11, 9, 629, 11, 630, 631, 11, + 242, 9, 633, 643, 9, 644, 11, 9, 629, 11, 645, 9, 646, 647, 648, 649, + 11, 650, 9, 651, 652, 653, 654, 11, 655, 9, 656, 11, 657, 539, 539, 539, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 32, 32, 32, 658, 32, 32, 659, 660, 661, 662, 45, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 663, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 666, 667, 668, 27, 27, 27, 669, 140, 670, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 151, 671, 672, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 673, 140, 48, 48, 674, 675, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 676, 192, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 590, 677, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 200, 678, 679, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 680, 200, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 681, 621, 140, 140, + 9, 9, 633, 11, 682, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 504, 272, 272, 683, 684, 140, 140, 140, 140, + 504, 272, 685, 686, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 687, 48, 688, 689, 690, 691, 692, 693, 694, 206, 695, 206, 140, 140, 140, 696, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 697, 209, 209, 209, 209, 209, 209, 322, 333, 698, 698, 698, 209, 323, + 699, 209, 209, 209, 209, 209, 209, 209, 209, 209, 700, 140, 140, 140, 701, 209, + 702, 209, 209, 697, 703, 704, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 705, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 706, 426, 426, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 176, 697, 427, + 697, 209, 209, 209, 707, 176, 209, 209, 707, 209, 700, 697, 704, 708, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 707, 700, 426, 709, 209, 209, 209, 710, 711, 712, 703, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 713, 209, 209, 209, 209, 209, 714, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, - 48, 48, 48, 207, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 481, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 100, 48, 48, 48, 48, 48, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, - 704, 140, 584, 584, 584, 584, 584, 584, 140, 140, 140, 140, 140, 140, 140, 140, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 705, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 706, - 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, - 11, 11, 12, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 57, 58, 59, 60, 60, 60, 60, 61, 62, 63, 64, 65, 66, 67, 68, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, - 92, 93, 94, 95, 96, 97, 98, 7, 4, 4, 4, 4, 99, 100, 101, 102, - 103, 104, 105, 106, 107, 108, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 110, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 112, 112, 112, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 114, 0, - 115, 116, 117, 118, 119, 120, 121, 122, 0, 123, 124, 125, 126, 126, 126, 127, - 128, 129, 130, 131, 132, 60, 133, 134, 135, 136, 0, 137, 138, 139, 0, 0, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 0, 126, 126, 126, 126, 126, 126, 126, 126, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 141, 142, 143, 143, 143, 143, 144, 11, 145, 146, 147, 4, 148, 149, - 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 166, 167, - 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 126, 126, 126, 126, 126, 169, 126, 170, 171, 172, 19, 173, - 19, 19, 19, 19, 174, 19, 175, 176, 177, 178, 19, 179, 180, 181, 182, 183, - 184, 185, 186, 187, 188, 189, 190, 191, 168, 168, 192, 193, 194, 195, 196, 197, - 198, 199, 200, 201, 202, 203, 204, 205, 206, 206, 206, 206, 207, 208, 209, 168, - 210, 211, 212, 213, 214, 168, 215, 216, 217, 218, 219, 220, 221, 222, 223, 168, - 224, 225, 226, 227, 228, 229, 230, 168, 168, 231, 232, 233, 234, 235, 236, 237, - 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, - 254, 255, 256, 257, 168, 168, 258, 259, 260, 261, 262, 263, 264, 265, 168, 168, - 266, 168, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 168, 168, 278, - 279, 280, 281, 168, 282, 283, 284, 168, 168, 168, 168, 285, 286, 287, 288, 289, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, 168, - 290, 292, 290, 290, 290, 293, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 294, 295, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 297, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, - 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 298, - 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 168, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 301, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 302, 302, 302, 302, 302, 302, 302, 302, 303, 304, 305, 306, 307, 308, 309, 168, - 168, 168, 168, 168, 168, 310, 168, 168, 168, 311, 312, 168, 313, 314, 315, 316, - 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, - 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 318, - 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 319, 319, 319, 319, - 319, 319, 319, 320, 321, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 322, - 323, 324, 324, 324, 325, 326, 327, 327, 327, 327, 327, 328, 168, 168, 168, 168, - 329, 330, 331, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 0, 0, 0, 332, 0, 0, 0, 0, 0, 0, 333, 168, 334, 335, 0, 336, - 0, 0, 0, 337, 338, 339, 340, 341, 189, 342, 168, 343, 0, 344, 168, 168, - 0, 345, 346, 347, 348, 349, 0, 0, 0, 0, 350, 0, 0, 0, 0, 351, - 352, 352, 352, 352, 352, 352, 352, 352, 352, 352, 353, 168, 168, 168, 168, 168, - 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 354, 168, 168, 168, - 355, 356, 357, 168, 358, 359, 168, 168, 168, 168, 360, 361, 168, 168, 168, 168, - 168, 168, 168, 362, 168, 168, 168, 363, 168, 168, 168, 168, 168, 168, 168, 364, - 365, 365, 365, 366, 367, 368, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, - 168, 369, 370, 168, 371, 168, 168, 168, 372, 373, 374, 375, 168, 168, 168, 168, - 376, 0, 377, 378, 0, 0, 379, 380, 381, 382, 168, 168, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 383, 0, 384, 0, 385, - 386, 387, 388, 389, 0, 0, 0, 0, 0, 390, 391, 392, 0, 0, 393, 332, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 394, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 395, 126, 126, 126, - 396, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 397, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 398, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 399, 168, 168, 168, 168, 168, 168, - 126, 126, 126, 126, 126, 126, 126, 126, 399, 168, 168, 168, 168, 168, 168, 168, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 400, 126, 126, - 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 401, 168, - 402, 0, 168, 168, 7, 7, 7, 403, 0, 1, 2, 3, 4, 4, 4, 4, + 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 715, 140, 587, 587, 587, 587, 587, 587, 140, 140, 140, 140, 140, 140, 140, 140, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 716, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 717, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 4, 0, 4, 2, 2, 5, 2, 2, 2, 5, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, - 0, 0, 0, 0, 7, 8, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, - 14, 14, 14, 14, 16, 17, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, - 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 21, - 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, - 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, - 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 29, 31, 31, 31, 31, 37, 38, 37, 37, 37, 37, 37, 37, - 37, 39, 31, 31, 31, 31, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, - 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, - 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 52, 31, 31, 31, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, 59, 60, 61, 62, - 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, 71, 72, 73, 74, - 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, 83, 84, 85, 86, - 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, 95, 96, 97, 98, - 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, 107, 104, 108, 109, - 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, 113, 115, 113, 116, - 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, 122, 124, 125, 126, - 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, 131, 131, 131, 131, - 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, 137, 137, 140, 141, - 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, 147, 147, 147, 148, - 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, 153, 152, 152, 154, - 155, 156, 152, 157, 26, 26, 26, 26, 158, 158, 158, 158, 158, 158, 158, 158, - 158, 159, 158, 158, 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, - 158, 161, 162, 163, 26, 26, 26, 26, 164, 164, 164, 164, 164, 164, 164, 164, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 6, 0, 0, 0, 0, 7, 8, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 11, + 12, 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 16, 17, 14, 14, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 20, 21, 21, 21, 22, 20, 21, 21, 21, 21, + 21, 23, 24, 25, 25, 25, 25, 25, 25, 26, 25, 25, 25, 27, 28, 26, + 29, 30, 31, 32, 31, 31, 31, 31, 33, 34, 35, 31, 31, 31, 36, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 29, 31, 31, 31, 31, + 37, 38, 37, 37, 37, 37, 37, 37, 37, 39, 31, 31, 31, 31, 31, 31, + 40, 40, 40, 40, 40, 40, 41, 26, 42, 42, 42, 42, 42, 42, 42, 43, + 44, 44, 44, 44, 44, 45, 44, 46, 47, 47, 47, 48, 37, 49, 31, 31, + 31, 31, 50, 31, 31, 31, 31, 31, 31, 31, 31, 31, 51, 31, 31, 31, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 52, 54, 52, 52, 52, + 55, 56, 57, 58, 58, 59, 60, 61, 56, 62, 63, 64, 65, 58, 58, 66, + 67, 68, 69, 70, 70, 71, 72, 73, 68, 74, 75, 76, 77, 70, 78, 26, + 79, 80, 81, 82, 82, 83, 84, 85, 80, 86, 87, 26, 88, 82, 89, 90, + 91, 92, 93, 94, 94, 95, 96, 97, 92, 98, 99, 100, 101, 94, 94, 26, + 102, 103, 104, 105, 106, 103, 107, 108, 103, 104, 109, 26, 110, 107, 107, 111, + 112, 113, 114, 112, 112, 114, 112, 115, 113, 116, 117, 118, 119, 112, 120, 112, + 121, 122, 123, 121, 121, 123, 124, 125, 122, 126, 127, 128, 129, 121, 130, 26, + 131, 132, 133, 131, 131, 131, 131, 131, 132, 133, 134, 131, 135, 131, 131, 131, + 136, 137, 138, 139, 137, 137, 140, 141, 138, 142, 143, 137, 144, 137, 145, 26, + 146, 147, 147, 147, 147, 147, 147, 148, 147, 147, 147, 149, 26, 26, 26, 26, + 150, 151, 152, 152, 153, 152, 152, 154, 155, 156, 152, 157, 26, 26, 26, 26, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 158, 158, 158, 160, 159, 158, + 158, 158, 158, 159, 158, 158, 158, 161, 158, 161, 162, 163, 26, 26, 26, 26, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, 166, 167, 165, 165, 165, 165, 165, 168, - 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, - 170, 171, 172, 171, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 172, - 171, 170, 172, 170, 170, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 170, - 170, 170, 170, 173, 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, - 176, 176, 176, 176, 176, 176, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, + 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, + 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 172, 171, 170, 170, 170, 170, + 170, 171, 170, 170, 170, 170, 171, 172, 171, 170, 172, 170, 170, 170, 170, 170, + 170, 170, 171, 170, 170, 170, 170, 170, 170, 170, 170, 173, 170, 170, 170, 174, + 170, 170, 170, 175, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 182, 181, 183, 184, 184, 185, 186, 187, 187, 188, 26, 189, 189, 190, 26, 191, 192, 193, 26, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 195, 194, 196, 194, 196, @@ -2466,165 +2264,215 @@ _hb_ucd_u16[10400] = 203, 203, 203, 204, 203, 205, 203, 205, 206, 203, 207, 207, 207, 208, 209, 26, 210, 210, 210, 210, 210, 211, 210, 210, 210, 212, 210, 213, 194, 194, 194, 194, 214, 214, 214, 215, 216, 216, 216, 216, 216, 216, 216, 217, 216, 216, 216, 218, - 216, 219, 216, 219, 216, 220, 9, 9, 9, 221, 26, 26, 26, 26, 26, 26, - 222, 222, 222, 222, 222, 222, 222, 222, 222, 223, 222, 222, 222, 222, 222, 222, - 224, 224, 224, 224, 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, 226, 227, - 228, 228, 228, 228, 228, 228, 228, 229, 228, 230, 231, 231, 231, 231, 231, 231, - 18, 232, 165, 165, 165, 165, 165, 233, 224, 26, 234, 9, 235, 236, 237, 238, - 2, 2, 2, 2, 239, 240, 2, 2, 2, 2, 2, 241, 242, 243, 2, 244, - 2, 2, 2, 2, 2, 2, 2, 245, 14, 14, 246, 246, 14, 14, 14, 14, - 246, 246, 14, 247, 14, 14, 14, 246, 14, 14, 14, 14, 14, 14, 248, 14, - 248, 14, 249, 250, 14, 14, 251, 252, 0, 253, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 254, 0, 255, 256, 0, 257, 2, 258, 0, 0, 0, 0, - 259, 26, 9, 9, 9, 9, 260, 26, 0, 0, 0, 0, 261, 262, 4, 0, - 0, 263, 0, 0, 2, 2, 2, 2, 2, 264, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 265, 26, 26, 0, 266, 26, 26, 0, 0, 0, 0, - 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, 0, 0, 0, 268, 0, - 0, 0, 269, 0, 0, 0, 0, 0, 270, 270, 270, 270, 270, 270, 270, 270, - 270, 270, 270, 270, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 271, 272, 165, 165, 165, 165, 166, 167, 273, 273, - 273, 273, 273, 273, 273, 274, 275, 274, 170, 170, 172, 26, 172, 172, 172, 172, - 172, 172, 172, 172, 18, 18, 18, 18, 0, 0, 0, 276, 26, 26, 26, 26, - 277, 277, 277, 278, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 279, 26, - 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 26, 26, 26, 0, 0, - 281, 0, 0, 0, 282, 283, 0, 284, 285, 286, 286, 286, 286, 286, 286, 286, - 286, 286, 287, 288, 289, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, - 292, 293, 293, 293, 293, 293, 294, 169, 169, 295, 0, 0, 293, 293, 293, 293, - 0, 0, 0, 0, 276, 296, 290, 290, 169, 169, 169, 295, 0, 0, 0, 0, - 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 290, 290, 290, 290, 290, 298, - 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 0, 0, 0, 0, 0, - 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 299, 299, 299, 299, 299, 299, - 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 303, 303, 303, - 303, 303, 303, 303, 303, 304, 26, 26, 18, 18, 18, 18, 305, 305, 305, 305, - 305, 305, 305, 305, 305, 305, 305, 26, 0, 0, 0, 0, 306, 2, 2, 2, - 2, 307, 2, 2, 2, 2, 2, 2, 2, 308, 309, 258, 26, 26, 310, 2, - 311, 311, 311, 311, 311, 312, 0, 265, 313, 313, 313, 313, 313, 313, 313, 26, - 314, 314, 314, 314, 314, 314, 314, 314, 315, 316, 314, 317, 53, 53, 53, 53, - 318, 318, 318, 318, 318, 319, 320, 320, 320, 320, 321, 322, 169, 169, 169, 323, - 324, 324, 324, 324, 324, 324, 324, 324, 324, 325, 324, 326, 164, 164, 164, 327, - 328, 328, 328, 328, 328, 328, 329, 26, 328, 330, 328, 331, 164, 164, 164, 164, - 332, 332, 332, 332, 332, 332, 332, 332, 333, 26, 26, 334, 335, 335, 336, 26, - 337, 337, 337, 26, 172, 172, 2, 2, 2, 2, 2, 338, 339, 340, 176, 176, - 176, 176, 176, 176, 176, 176, 176, 176, 335, 335, 335, 335, 335, 341, 335, 342, - 169, 169, 169, 169, 343, 26, 169, 169, 295, 344, 169, 169, 169, 169, 169, 343, - 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 280, 277, 277, - 277, 277, 277, 345, 26, 26, 26, 26, 346, 26, 347, 348, 25, 25, 349, 350, - 351, 25, 31, 31, 31, 31, 31, 31, 352, 26, 353, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 354, 31, 31, 355, 31, 31, 31, 31, 31, - 31, 356, 26, 26, 26, 26, 31, 31, 9, 9, 0, 265, 9, 357, 0, 0, - 0, 0, 358, 0, 257, 359, 360, 31, 31, 31, 31, 31, 31, 31, 31, 361, - 362, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 363, 290, 289, 290, - 290, 290, 290, 364, 169, 169, 169, 295, 365, 365, 365, 366, 257, 257, 26, 367, - 368, 369, 368, 368, 370, 368, 368, 371, 368, 372, 368, 372, 26, 26, 26, 26, - 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 373, - 374, 0, 0, 0, 0, 0, 375, 0, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 252, 0, 376, 377, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 378, - 379, 379, 379, 380, 381, 381, 381, 381, 381, 381, 382, 26, 383, 0, 0, 359, - 384, 384, 384, 384, 385, 386, 387, 387, 387, 388, 389, 389, 389, 389, 389, 390, - 391, 391, 391, 392, 393, 393, 393, 393, 394, 393, 395, 26, 26, 26, 26, 26, - 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 397, 397, 397, 397, 397, 397, - 398, 398, 398, 399, 398, 400, 401, 401, 401, 401, 402, 401, 401, 401, 401, 402, - 403, 403, 403, 403, 403, 26, 404, 404, 404, 404, 404, 404, 405, 406, 407, 408, - 407, 408, 409, 407, 410, 407, 410, 411, 412, 412, 412, 412, 412, 412, 413, 26, - 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 415, 26, - 414, 414, 416, 26, 414, 26, 26, 26, 417, 2, 2, 2, 2, 2, 418, 419, - 420, 421, 422, 422, 422, 422, 423, 424, 425, 425, 426, 425, 427, 427, 427, 427, - 428, 428, 428, 429, 430, 428, 26, 26, 26, 26, 26, 26, 431, 431, 432, 433, - 434, 434, 434, 435, 436, 436, 436, 437, 438, 438, 438, 438, 439, 439, 439, 440, - 439, 439, 441, 439, 439, 439, 439, 439, 442, 443, 444, 445, 446, 446, 447, 448, - 446, 449, 446, 449, 450, 450, 450, 450, 451, 451, 451, 451, 26, 26, 26, 26, - 452, 452, 452, 452, 453, 454, 453, 26, 455, 455, 455, 455, 455, 455, 456, 457, - 458, 458, 459, 458, 460, 460, 461, 460, 462, 462, 463, 464, 26, 465, 26, 26, - 466, 466, 466, 466, 466, 466, 466, 466, 466, 467, 26, 26, 26, 26, 26, 26, - 468, 468, 468, 468, 468, 468, 469, 26, 468, 468, 468, 468, 468, 468, 469, 470, - 471, 471, 471, 471, 471, 26, 471, 472, 473, 473, 473, 473, 474, 475, 473, 473, - 474, 476, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 31, 31, 31, 50, - 477, 477, 477, 477, 477, 478, 479, 26, 480, 26, 26, 26, 26, 26, 26, 481, - 482, 482, 482, 482, 482, 26, 483, 483, 483, 483, 483, 484, 26, 26, 485, 485, - 485, 486, 26, 26, 26, 26, 487, 487, 487, 488, 26, 26, 489, 489, 490, 26, - 491, 491, 491, 491, 491, 491, 491, 491, 491, 492, 493, 491, 491, 491, 492, 494, - 495, 495, 495, 495, 495, 495, 495, 495, 496, 497, 498, 498, 498, 499, 498, 500, - 501, 501, 501, 501, 501, 501, 502, 501, 501, 26, 503, 503, 503, 503, 504, 26, - 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 506, 137, 507, 26, - 508, 508, 509, 508, 508, 508, 508, 508, 510, 26, 26, 26, 26, 26, 26, 26, - 511, 512, 513, 514, 513, 515, 516, 516, 516, 516, 516, 516, 516, 517, 516, 518, - 519, 520, 521, 522, 522, 523, 524, 525, 520, 526, 527, 528, 529, 530, 530, 26, - 531, 532, 531, 531, 531, 531, 533, 531, 534, 535, 533, 536, 537, 26, 26, 26, - 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 539, 540, 26, 26, 26, - 541, 541, 541, 541, 541, 541, 541, 541, 541, 26, 541, 542, 26, 26, 26, 26, - 543, 543, 543, 543, 543, 543, 544, 543, 543, 543, 543, 544, 26, 26, 26, 26, - 545, 545, 545, 545, 545, 545, 545, 545, 546, 26, 545, 547, 198, 548, 26, 26, - 549, 549, 549, 549, 549, 549, 549, 550, 549, 550, 164, 164, 551, 26, 26, 26, - 552, 552, 552, 553, 552, 554, 552, 552, 555, 26, 26, 26, 26, 26, 26, 26, - 556, 556, 556, 556, 556, 556, 556, 557, 26, 26, 26, 26, 558, 558, 558, 558, - 558, 558, 558, 558, 558, 558, 559, 560, 561, 562, 563, 564, 564, 564, 565, 566, - 561, 26, 564, 567, 26, 26, 26, 26, 26, 26, 26, 26, 568, 569, 568, 568, - 568, 568, 568, 569, 570, 26, 26, 26, 571, 571, 571, 571, 571, 571, 571, 571, - 571, 26, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 573, 26, 178, 178, - 574, 574, 574, 574, 574, 574, 574, 575, 53, 576, 26, 26, 26, 26, 26, 26, - 577, 577, 577, 577, 578, 26, 577, 578, 579, 580, 579, 579, 579, 579, 581, 579, - 582, 26, 579, 579, 579, 583, 584, 584, 584, 584, 585, 584, 584, 586, 587, 26, - 588, 589, 590, 590, 590, 590, 588, 591, 590, 26, 590, 592, 593, 594, 595, 595, - 595, 596, 597, 598, 595, 599, 26, 26, 26, 26, 26, 26, 600, 600, 600, 601, - 602, 602, 603, 602, 602, 602, 602, 604, 602, 602, 602, 605, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 606, 26, 108, 108, 108, 108, 108, 108, 607, 608, - 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 610, 26, 26, 26, 26, - 609, 609, 609, 609, 609, 611, 612, 26, 613, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 615, 26, - 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 617, 26, 616, 616, 616, 616, - 616, 616, 616, 616, 616, 616, 616, 618, 619, 619, 619, 619, 619, 619, 619, 619, - 620, 26, 26, 26, 26, 26, 26, 26, 621, 621, 621, 621, 621, 621, 621, 622, - 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 623, - 624, 624, 624, 625, 624, 626, 627, 627, 627, 627, 627, 627, 627, 627, 627, 628, - 627, 629, 630, 630, 630, 631, 631, 26, 632, 632, 632, 632, 632, 632, 632, 632, - 633, 26, 632, 634, 634, 632, 632, 635, 632, 632, 26, 26, 26, 26, 26, 26, - 636, 636, 636, 636, 636, 636, 636, 637, 638, 638, 638, 638, 638, 638, 638, 638, - 638, 638, 638, 639, 26, 26, 26, 26, 640, 640, 640, 640, 640, 640, 640, 640, - 640, 641, 640, 640, 640, 640, 640, 640, 640, 642, 640, 640, 26, 26, 26, 26, - 26, 26, 26, 26, 643, 26, 345, 26, 644, 644, 644, 644, 644, 644, 644, 644, - 644, 644, 644, 644, 644, 644, 644, 26, 645, 645, 645, 645, 645, 645, 645, 645, - 645, 645, 646, 26, 26, 26, 26, 647, 644, 648, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 649, 650, 651, 286, 286, 286, 286, 286, 286, 286, - 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 652, 26, 653, 26, - 26, 26, 654, 26, 655, 26, 656, 656, 656, 656, 656, 656, 656, 656, 656, 656, - 656, 656, 656, 656, 656, 656, 656, 657, 658, 658, 658, 658, 658, 658, 658, 658, - 658, 658, 658, 658, 658, 659, 658, 660, 658, 661, 658, 662, 359, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 359, 26, - 9, 9, 9, 9, 9, 663, 9, 9, 221, 26, 0, 0, 0, 0, 0, 0, - 359, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 276, 26, - 0, 0, 0, 0, 257, 362, 0, 0, 0, 0, 0, 0, 664, 665, 0, 666, - 667, 668, 0, 0, 0, 669, 0, 0, 0, 0, 0, 0, 0, 266, 26, 26, - 246, 26, 26, 26, 26, 26, 26, 26, 0, 0, 359, 26, 0, 0, 359, 26, - 0, 0, 257, 26, 0, 0, 0, 259, 0, 0, 254, 0, 0, 0, 0, 0, - 0, 0, 0, 254, 670, 671, 0, 672, 673, 0, 0, 0, 0, 0, 0, 0, - 269, 674, 254, 254, 0, 0, 0, 675, 676, 677, 678, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 276, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, - 679, 679, 679, 679, 679, 679, 679, 679, 679, 680, 26, 681, 682, 679, 26, 26, - 2, 2, 2, 346, 683, 419, 26, 26, 684, 270, 270, 685, 686, 687, 18, 18, - 18, 18, 18, 18, 18, 688, 26, 26, 26, 689, 26, 26, 26, 26, 26, 26, - 690, 690, 690, 690, 690, 691, 690, 692, 690, 693, 26, 26, 26, 26, 26, 26, - 26, 26, 694, 694, 694, 695, 26, 26, 696, 696, 696, 696, 696, 696, 696, 697, - 26, 26, 698, 698, 698, 698, 698, 699, 26, 26, 700, 700, 700, 700, 700, 701, - 26, 26, 26, 26, 172, 702, 170, 172, 703, 703, 703, 703, 703, 703, 703, 703, - 704, 703, 705, 26, 26, 26, 26, 26, 706, 706, 706, 706, 706, 706, 706, 706, - 706, 707, 706, 708, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 362, 0, - 0, 0, 0, 0, 0, 0, 376, 26, 362, 0, 0, 0, 0, 0, 0, 276, - 709, 31, 31, 31, 710, 711, 712, 713, 714, 715, 710, 716, 710, 712, 712, 717, - 31, 718, 31, 719, 720, 718, 31, 719, 26, 26, 26, 26, 26, 26, 721, 26, - 0, 0, 0, 0, 0, 359, 0, 0, 0, 0, 359, 26, 0, 257, 362, 0, - 362, 0, 362, 0, 0, 0, 276, 26, 0, 0, 0, 0, 0, 276, 26, 26, - 26, 26, 26, 26, 722, 0, 0, 0, 723, 26, 0, 0, 0, 0, 0, 359, - 0, 259, 265, 26, 276, 26, 26, 26, 0, 0, 0, 724, 0, 376, 0, 376, - 0, 0, 0, 0, 0, 0, 257, 725, 0, 0, 0, 265, 0, 359, 259, 26, - 0, 359, 0, 0, 0, 0, 0, 0, 0, 26, 0, 265, 0, 0, 0, 0, - 0, 26, 0, 0, 0, 276, 0, 359, 265, 26, 26, 26, 26, 26, 26, 26, - 0, 0, 359, 26, 0, 276, 0, 376, 0, 726, 0, 0, 0, 0, 0, 0, - 257, 722, 0, 727, 0, 265, 0, 259, 0, 0, 358, 0, 0, 0, 0, 0, - 277, 277, 277, 277, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 345, - 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 345, 26, 277, 277, - 277, 277, 277, 277, 728, 26, 277, 277, 277, 277, 277, 280, 26, 26, 26, 26, - 277, 729, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 26, 26, - 730, 26, 26, 26, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 0, 0, + 216, 219, 216, 219, 216, 220, 9, 9, 9, 9, 9, 221, 9, 222, 26, 26, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 224, 223, 223, 223, 223, 223, 223, + 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, 226, 226, 226, 226, 227, 228, + 229, 229, 229, 229, 229, 229, 229, 230, 229, 231, 232, 232, 232, 232, 232, 232, + 18, 233, 165, 165, 165, 165, 165, 234, 225, 26, 235, 9, 236, 237, 238, 239, + 2, 2, 2, 2, 240, 241, 2, 2, 2, 2, 2, 242, 243, 244, 2, 245, + 2, 2, 2, 2, 2, 2, 2, 246, 9, 9, 9, 9, 9, 9, 9, 9, + 14, 14, 247, 247, 14, 14, 14, 14, 247, 247, 14, 248, 14, 14, 14, 247, + 14, 14, 14, 14, 14, 14, 249, 14, 249, 14, 250, 251, 14, 14, 252, 253, + 0, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 256, 257, + 0, 258, 2, 259, 0, 0, 0, 0, 260, 26, 9, 9, 9, 9, 261, 26, + 0, 0, 0, 0, 262, 263, 4, 0, 0, 264, 0, 0, 2, 2, 2, 2, + 2, 265, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 260, 26, 26, 0, 266, 26, 26, 0, 0, 0, 0, + 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 2, 2, 2, 2, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 270, 271, + 165, 165, 165, 165, 166, 167, 272, 272, 272, 272, 272, 272, 272, 273, 274, 273, + 170, 170, 172, 26, 172, 172, 172, 172, 172, 172, 172, 172, 18, 18, 18, 18, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, + 276, 276, 276, 277, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 278, 26, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 26, 26, 26, 0, 0, + 280, 0, 0, 0, 281, 282, 0, 283, 284, 285, 285, 285, 285, 285, 285, 285, + 285, 285, 286, 287, 288, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 290, + 291, 292, 292, 292, 292, 292, 293, 169, 169, 169, 169, 169, 169, 169, 169, 169, + 169, 294, 0, 0, 292, 292, 292, 292, 0, 0, 0, 0, 275, 295, 289, 289, + 169, 169, 169, 294, 0, 0, 0, 0, 0, 0, 0, 0, 169, 169, 169, 296, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 289, 289, 289, 289, 297, + 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 0, 0, 0, 0, 0, + 276, 276, 276, 276, 276, 276, 276, 276, 0, 0, 0, 0, 0, 0, 0, 0, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 299, 298, 298, 298, 298, 298, 298, 300, 26, 301, 301, 301, 301, 301, 301, + 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, + 302, 302, 302, 302, 302, 303, 26, 26, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 26, + 0, 0, 0, 0, 305, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 306, 2, 2, 2, 2, 2, 2, 2, 2, 2, 259, 26, 26, 307, 2, + 308, 308, 308, 308, 308, 309, 0, 260, 310, 310, 310, 310, 310, 310, 310, 26, + 311, 311, 311, 311, 311, 311, 311, 311, 312, 313, 311, 314, 52, 52, 52, 52, + 315, 315, 315, 315, 315, 316, 317, 317, 317, 317, 318, 319, 169, 169, 169, 320, + 321, 321, 321, 321, 321, 321, 321, 321, 321, 322, 321, 323, 164, 164, 164, 324, + 325, 325, 325, 325, 325, 325, 326, 26, 325, 327, 325, 328, 164, 164, 164, 164, + 329, 329, 329, 329, 329, 329, 329, 329, 330, 26, 26, 331, 332, 332, 333, 26, + 334, 334, 334, 26, 172, 172, 2, 2, 2, 2, 2, 335, 336, 337, 176, 176, + 176, 176, 176, 176, 176, 176, 176, 176, 332, 332, 332, 332, 332, 338, 332, 339, + 169, 169, 169, 169, 340, 26, 169, 169, 294, 341, 169, 169, 169, 169, 169, 340, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 342, 26, 26, 26, 26, + 343, 26, 344, 345, 25, 25, 346, 347, 348, 25, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 349, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 26, 26, 26, 26, 31, 31, + 9, 9, 0, 260, 9, 350, 0, 0, 0, 0, 351, 0, 258, 352, 353, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 354, + 355, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 356, 289, 288, 289, + 289, 289, 289, 357, 169, 169, 169, 294, 358, 358, 358, 359, 258, 258, 26, 360, + 361, 362, 361, 361, 363, 361, 361, 364, 361, 365, 361, 365, 26, 26, 26, 26, + 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 366, + 367, 0, 0, 0, 0, 0, 368, 0, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 253, 0, 369, 370, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 371, + 372, 372, 372, 373, 374, 374, 374, 374, 374, 374, 375, 26, 376, 0, 0, 352, + 377, 377, 377, 377, 378, 379, 380, 380, 380, 381, 382, 382, 382, 382, 382, 383, + 384, 384, 384, 385, 386, 386, 386, 386, 387, 386, 388, 26, 26, 26, 26, 26, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 390, 390, 390, 390, 390, 390, + 391, 391, 391, 392, 391, 393, 394, 394, 394, 394, 395, 394, 394, 394, 394, 395, + 396, 396, 396, 396, 396, 26, 397, 397, 397, 397, 397, 397, 398, 399, 400, 401, + 400, 401, 402, 400, 403, 400, 403, 404, 405, 405, 405, 405, 405, 405, 406, 26, + 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, + 407, 407, 407, 407, 407, 407, 408, 26, 407, 407, 409, 26, 407, 26, 26, 26, + 410, 2, 2, 2, 2, 2, 411, 412, 26, 26, 26, 26, 26, 26, 26, 26, + 413, 414, 415, 415, 415, 415, 416, 417, 418, 418, 419, 418, 420, 420, 420, 420, + 421, 421, 421, 422, 423, 421, 26, 26, 26, 26, 26, 26, 424, 424, 425, 426, + 427, 427, 427, 428, 429, 429, 429, 430, 431, 431, 431, 432, 26, 26, 26, 26, + 433, 433, 433, 433, 434, 434, 434, 435, 434, 434, 436, 434, 434, 434, 434, 434, + 437, 438, 439, 440, 441, 441, 442, 443, 441, 444, 441, 444, 445, 445, 445, 445, + 446, 446, 446, 446, 26, 26, 26, 26, 447, 447, 447, 447, 448, 449, 448, 26, + 450, 450, 450, 450, 450, 450, 451, 452, 453, 453, 454, 453, 455, 455, 456, 455, + 457, 457, 458, 459, 26, 460, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 461, 461, 461, 461, 461, 461, 461, 461, 461, 462, 26, 26, 26, 26, 26, 26, + 463, 463, 463, 463, 463, 463, 464, 26, 463, 463, 463, 463, 463, 463, 464, 465, + 466, 466, 466, 466, 466, 26, 466, 467, 468, 468, 468, 468, 469, 470, 468, 468, + 469, 471, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 31, 31, 31, 472, + 473, 473, 473, 473, 473, 474, 475, 26, 476, 26, 31, 477, 26, 26, 26, 476, + 478, 478, 478, 478, 478, 26, 479, 479, 479, 479, 479, 480, 26, 26, 481, 481, + 481, 482, 26, 26, 26, 26, 483, 483, 483, 484, 26, 26, 485, 485, 486, 26, + 487, 487, 487, 487, 487, 487, 487, 487, 487, 488, 489, 487, 487, 487, 488, 490, + 491, 491, 491, 491, 491, 491, 491, 491, 492, 493, 494, 494, 494, 495, 494, 496, + 497, 497, 497, 497, 497, 497, 498, 497, 497, 26, 499, 499, 499, 499, 500, 26, + 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 502, 137, 503, 26, + 504, 504, 505, 504, 504, 504, 504, 504, 506, 26, 26, 26, 26, 26, 26, 26, + 507, 508, 509, 510, 509, 511, 512, 512, 512, 512, 512, 512, 512, 513, 512, 514, + 515, 516, 517, 518, 518, 519, 520, 521, 516, 522, 523, 524, 525, 526, 526, 26, + 527, 528, 527, 527, 527, 527, 529, 527, 530, 531, 529, 532, 533, 26, 26, 26, + 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 535, 536, 26, 26, 26, + 537, 537, 537, 537, 537, 537, 537, 537, 537, 26, 537, 538, 26, 26, 26, 26, + 539, 539, 539, 539, 539, 539, 540, 539, 539, 539, 539, 540, 26, 26, 26, 26, + 541, 541, 541, 541, 541, 541, 541, 541, 542, 26, 541, 543, 198, 544, 26, 26, + 545, 545, 545, 545, 545, 545, 545, 546, 545, 546, 164, 164, 547, 26, 26, 26, + 548, 548, 548, 549, 548, 550, 548, 548, 551, 26, 26, 26, 26, 26, 26, 26, + 552, 552, 552, 552, 552, 552, 552, 553, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 555, 556, + 557, 558, 559, 560, 560, 560, 561, 562, 557, 26, 560, 563, 26, 26, 26, 26, + 26, 26, 26, 26, 564, 565, 564, 564, 564, 564, 564, 565, 566, 26, 26, 26, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 26, 568, 568, 568, 568, 568, 568, + 568, 568, 568, 568, 569, 26, 178, 178, 570, 570, 570, 570, 570, 570, 570, 571, + 52, 572, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 501, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 573, 573, 573, 573, 574, 26, 573, 574, + 575, 576, 575, 575, 575, 575, 577, 575, 578, 26, 575, 575, 575, 579, 580, 580, + 580, 580, 581, 580, 580, 582, 583, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 584, 585, 586, 586, 586, 586, 584, 587, 586, 26, 586, 588, 589, 590, 591, 591, + 591, 592, 593, 594, 591, 595, 596, 596, 596, 596, 596, 597, 596, 598, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 599, 599, 599, 600, + 601, 601, 602, 601, 601, 601, 601, 603, 601, 601, 601, 604, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 605, 26, 107, 107, 107, 107, 107, 107, 606, 607, + 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, + 608, 608, 608, 609, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 610, 611, 26, + 608, 608, 608, 608, 608, 608, 608, 608, 612, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 614, 26, + 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, + 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 616, 26, 615, 615, 615, 615, + 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 617, + 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, + 618, 618, 618, 618, 618, 618, 618, 618, 619, 26, 26, 26, 26, 26, 26, 26, + 620, 620, 620, 620, 620, 620, 620, 621, 26, 26, 26, 26, 26, 26, 26, 26, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 622, 623, 623, 623, 624, 623, 625, 626, 626, + 626, 626, 626, 626, 626, 626, 626, 627, 626, 628, 629, 629, 629, 630, 630, 26, + 631, 631, 631, 631, 631, 631, 631, 631, 632, 26, 631, 633, 633, 631, 631, 634, + 631, 631, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 635, 635, 635, 635, 635, 635, 635, 636, + 26, 26, 26, 26, 26, 26, 26, 26, 637, 637, 637, 637, 637, 637, 637, 637, + 637, 637, 637, 638, 639, 639, 639, 640, 639, 639, 641, 26, 26, 26, 26, 26, + 642, 642, 642, 642, 642, 642, 642, 642, 642, 643, 642, 642, 642, 642, 642, 642, + 642, 644, 642, 642, 26, 26, 26, 26, 26, 26, 26, 26, 645, 26, 646, 26, + 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, + 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, + 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 649, 26, 26, 26, 26, 650, + 647, 647, 647, 651, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 652, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 653, 654, + 655, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, + 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, + 285, 285, 285, 285, 656, 26, 657, 26, 26, 26, 658, 26, 659, 26, 660, 660, + 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, + 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 661, + 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 663, 662, 664, + 662, 665, 662, 666, 352, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, + 0, 0, 0, 0, 0, 0, 352, 667, 0, 0, 668, 26, 0, 0, 668, 26, + 9, 9, 9, 9, 9, 221, 9, 9, 669, 26, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 352, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, 26, + 0, 0, 0, 0, 258, 355, 0, 0, 0, 0, 0, 0, 670, 671, 0, 672, + 673, 674, 0, 0, 0, 675, 0, 0, 0, 0, 0, 0, 0, 266, 26, 26, + 14, 14, 14, 14, 14, 14, 14, 14, 247, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 352, 26, 0, 0, 352, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 26, 0, 0, 0, 668, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 676, 677, 0, 678, 679, 0, 0, 0, 0, 0, 0, 0, + 680, 681, 255, 255, 0, 0, 0, 682, 683, 667, 684, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, + 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, + 685, 686, 26, 687, 688, 685, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 2, 2, 2, 343, 689, 412, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 690, 269, 269, 691, 692, 693, 18, 18, 18, 18, 18, 18, 18, 694, 26, 26, + 26, 695, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 696, 696, 696, 696, 696, 697, 696, 698, 696, 699, 26, 26, 26, 26, 26, 26, + 26, 26, 700, 700, 700, 701, 26, 26, 702, 702, 702, 702, 702, 702, 702, 703, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 704, 704, 704, 704, 704, 705, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 706, 706, 706, 706, 706, 707, + 26, 26, 26, 26, 26, 26, 26, 26, 708, 708, 708, 709, 708, 708, 710, 711, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 172, 712, 170, 172, + 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, + 713, 713, 713, 713, 713, 713, 713, 713, 714, 713, 715, 26, 26, 26, 26, 26, + 716, 716, 716, 716, 716, 716, 716, 716, 716, 717, 716, 718, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 355, 0, + 0, 0, 0, 0, 0, 0, 369, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 355, 0, 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, 26, 26, 26, 26, + 719, 31, 31, 31, 720, 721, 722, 723, 724, 725, 720, 726, 720, 722, 722, 727, + 31, 728, 31, 729, 730, 728, 31, 729, 26, 26, 26, 26, 26, 26, 731, 26, + 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 352, 26, 0, 258, 355, 0, 355, 0, 355, 0, 0, 0, 275, 26, + 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, 26, 26, 732, 0, 0, 0, + 733, 26, 0, 0, 0, 0, 0, 352, 0, 668, 260, 26, 275, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 734, 0, 369, 0, 369, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 260, 0, 352, 668, 26, + 0, 352, 0, 0, 0, 0, 0, 0, 0, 26, 0, 260, 0, 0, 0, 0, + 0, 26, 0, 0, 0, 275, 0, 352, 260, 26, 0, 668, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 275, 0, 369, + 0, 735, 0, 0, 0, 0, 0, 0, 258, 736, 0, 737, 0, 367, 0, 668, + 0, 0, 351, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 266, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 26, 26, 26, 26, + 276, 276, 276, 279, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 279, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 738, 26, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 26, 26, 26, 26, + 276, 276, 276, 279, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 739, 276, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 342, + 740, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, @@ -2840,8 +2688,7 @@ _hb_ucd_u16[10400] = 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, }; -static const int16_t -_hb_ucd_i16[196] = +static const int16_t _hb_ucd_i16[196]= { 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, @@ -2858,47 +2705,42 @@ _hb_ucd_i16[196] = -1, 0, 1, -1, }; -static inline uint_fast8_t -_hb_ucd_gc (unsigned u) +static inline uint8_t _hb_ucd_gc (unsigned u) { - return u<1114110u?_hb_ucd_u8[6472+(((_hb_ucd_u8[816+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>4>>4])<<4)+((u>>1>>3>>4)&15u))])<<4)+((u>>1>>3)&15u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; + return u<1114110 ? _hb_ucd_u8[7920u+((_hb_ucd_u8[2176u+((_hb_ucd_u16[((_hb_ucd_u8[((((((u)>>1))>>3))>>5)])<<5)+((((((u)>>1))>>3))&31)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; } -static inline uint_fast8_t -_hb_ucd_ccc (unsigned u) +static inline uint8_t _hb_ucd_ccc (unsigned u) { - return u<125259u?_hb_ucd_u8[8504+(((_hb_ucd_u8[7936+(((_hb_ucd_u8[7460+(((_hb_ucd_u8[7100+(((_hb_ucd_u8[6854+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; + return u<125259 ? _hb_ucd_u8[10388u+((_hb_ucd_u8[9284u+((_hb_ucd_u8[8548u+((_hb_ucd_u8[8302u+((((((u)>>2))>>3))>>4)])<<4)+((((((u)>>2))>>3))&15)])<<3)+((((u)>>2))&7)])<<2)+((u)&3)] : 0; } -static inline unsigned -_hb_ucd_b4 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline int_fast16_t -_hb_ucd_bmg (unsigned u) +static inline int16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9252+(((_hb_ucd_u8[9132+(((_hb_ucd_b4(9004+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; + return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[11140u+((_hb_ucd_u8[11020u+((_hb_ucd_b4(_hb_ucd_u8+10892u,((((((u)>>2))>>3))>>3)))<<3)+((((((u)>>2))>>3))&7)])<<3)+((((u)>>2))&7)])<<2)+((u)&3)] : 0; } -static inline uint_fast8_t -_hb_ucd_sc (unsigned u) +static inline uint8_t _hb_ucd_sc (unsigned u) { - return u<918000u?_hb_ucd_u8[10486+(((_hb_ucd_u16[3744+(((_hb_ucd_u16[2624+(((_hb_ucd_u8[9588+(u>>3>>3>>4)])<<4)+((u>>3>>3)&15u))])<<3)+((u>>3)&7u))])<<3)+((u)&7u))]:2; + return u<918000 ? _hb_ucd_u8[12662u+((_hb_ucd_u16[3328u+((_hb_ucd_u8[11926u+((_hb_ucd_u8[11476u+((((((u)>>3))>>4))>>4)])<<4)+((((((u)>>3))>>4))&15)])<<4)+((((u)>>3))&15)])<<3)+((u)&7)] : 2; } -static inline uint_fast16_t -_hb_ucd_dm (unsigned u) +static inline uint16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[6976+(((_hb_ucd_u8[16716+(((_hb_ucd_u8[16334+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102 ? _hb_ucd_u16[7408u+((_hb_ucd_u8[18972u+((_hb_ucd_u8[18590u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; } #elif !defined(HB_NO_UCD_UNASSIGNED) -static const uint8_t -_hb_ucd_u8[17524] = +#include + +static const uint8_t _hb_ucd_u8[14800]= { 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 18, 19, 20, 21, 22, 23, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 25, 26, 5, 27, 28, - 5, 29, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 5, 29, 5, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, @@ -2929,23 +2771,23 @@ _hb_ucd_u8[17524] = 17, 17, 17,103, 17, 17,104,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,105,100,100,100,100,100,100, 17, 17,106,107,100,108,109,110, - 17, 17, 17, 17, 17, 17, 17,111, 17, 17, 17, 17,112,113,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,114, - 17,115,116,100,100,100,100,100,100,100,100,100,117,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,118, 39,119,120, - 121,122,123,124,125,126,127,128, 39, 39,129,100,100,100,100,130, - 131,132,133,100,134,135,100,136,137,138,100,100,139,140,141,100, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,111,112,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,113, + 17,114,115,100,100,100,100,100,100,100,100,100,116,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,117, 39,118,119, + 120,121,122,123,124,125,126,127, 39, 39,128,100,100,100,100,129, + 130,131,132,100,133,134,135,136,137,138,100,100,139,140,141,100, 142,143,144,145, 39, 39,146,147,148, 39,149,150,100,100,100,100, 17, 17, 17, 17, 17, 17,151, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17,152,153, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,155, 17, 17,156,100, - 100,100,100,100,100,100,100,100, 17, 17,157,100,100,100,100,100, - 17, 17, 17,158, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,159,100,100,100,100,100,100,100,100,100,100,100,100, - 160,161,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 17, 17, 17, 17, 17, 17, 17, 17,152, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,153, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, 17,155,100, + 100,100,100,100,100,100,100,100, 17, 17,156,100,100,100,100,100, + 17, 17, 17,157, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17,158,100,100,100,100,100,100,100,100,100,100,100, + 159,160,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,161, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,162, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,163, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, @@ -2959,409 +2801,415 @@ _hb_ucd_u8[17524] = 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, - 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, - 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, - 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, - 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, - 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, - 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, - 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, - 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, - 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, - 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, - 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, - 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, - 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, - 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, - 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, - 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 57, 43, 43, 43, 43, - 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, - 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, - 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, - 36, 43, 2, 7, 7, 7, 7, 7, 88, 36, 36, 36, 36, 36, 36, 36, - 70, 86, 62, 36, 36, 36, 61, 62, 61, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 61, 36, 36, 36, 61, 61, 44, 36, 36, 44, 71, 86, - 87, 43, 80, 89, 90, 89, 87, 61, 44, 44, 44, 89, 44, 44, 36, 62, - 36, 43, 44, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 56, 63, 80, - 57, 85, 62, 36, 36, 61, 44, 62, 61, 36, 62, 61, 36, 44, 80, 86, - 87, 80, 44, 57, 80, 57, 43, 44, 57, 44, 44, 44, 62, 36, 61, 61, - 44, 44, 44, 7, 7, 7, 7, 7, 43, 36, 70, 64, 44, 44, 44, 44, - 57, 85, 62, 36, 36, 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, - 61, 36, 62, 36, 36, 44, 71, 86, 87, 43, 43, 57, 85, 89, 87, 44, - 61, 44, 44, 44, 44, 44, 44, 44, 66, 44, 44, 44, 62, 43, 43, 43, - 57, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 44, 71, 87, - 87, 43, 80, 89, 90, 89, 87, 44, 44, 44, 57, 85, 44, 44, 36, 62, - 78, 27, 27, 27, 44, 44, 44, 44, 44, 71, 62, 36, 36, 61, 44, 36, - 61, 36, 36, 44, 62, 61, 61, 36, 44, 62, 61, 44, 36, 61, 44, 36, - 36, 36, 36, 36, 36, 44, 44, 86, 85, 90, 44, 86, 90, 86, 87, 44, - 61, 44, 44, 89, 44, 44, 44, 44, 27, 91, 67, 67, 56, 92, 44, 44, - 85, 86, 71, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 44, 71, 43, 85, 86, 90, 43, 80, 43, 43, 44, - 44, 44, 57, 80, 36, 61, 62, 44, 44, 44, 44, 93, 27, 27, 27, 91, - 70, 86, 72, 36, 36, 36, 61, 36, 36, 36, 62, 36, 36, 44, 71, 87, - 86, 86, 90, 85, 90, 86, 43, 44, 44, 44, 89, 90, 44, 44, 62, 61, - 62, 94, 44, 44, 44, 44, 44, 44, 43, 86, 36, 36, 36, 36, 61, 36, - 36, 36, 36, 36, 36, 70, 71, 86, 87, 43, 80, 86, 90, 86, 87, 77, - 44, 44, 36, 94, 27, 27, 27, 95, 27, 27, 27, 27, 91, 36, 36, 36, - 57, 86, 62, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 36, 36, 36, - 36, 62, 36, 36, 36, 36, 62, 44, 36, 36, 36, 61, 44, 80, 44, 89, - 86, 43, 80, 80, 86, 86, 86, 86, 44, 86, 64, 44, 44, 44, 44, 44, - 62, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 96, - 36, 36, 36, 75, 43, 43, 43, 60, 7, 7, 7, 7, 7, 2, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, - 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, - 36, 36, 61, 81, 43, 43, 43, 80, 7, 7, 7, 7, 7, 44, 36, 36, - 77, 67, 2, 2, 2, 2, 2, 2, 2, 97, 97, 67, 43, 67, 67, 67, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 86, - 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, - 57, 43, 43, 43, 43, 43, 43, 85, 43, 43, 60, 43, 36, 36, 70, 43, - 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, - 67, 67, 67, 76, 67, 67, 92, 67, 2, 2, 97, 67, 21, 64, 44, 44, - 36, 36, 36, 36, 36, 94, 87, 43, 85, 43, 43, 43, 87, 85, 87, 71, - 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 86, 43, 36, 36, 43, - 71, 86, 98, 94, 86, 86, 86, 36, 70, 43, 71, 36, 36, 36, 36, 36, - 36, 85, 87, 85, 86, 86, 87, 94, 7, 7, 7, 7, 7, 86, 87, 67, - 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, - 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, - 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, - 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, - 2, 2, 2, 2, 99, 27, 27, 27, 27, 27, 27, 27, 27, 27,100, 44, - 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, - 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, - 102, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,103,104, 44, - 36, 36, 36, 36, 36, 63, 2,105,106, 36, 36, 36, 61, 44, 44, 44, - 36, 43, 85, 44, 44, 44, 44, 62, 36, 43,107, 64, 44, 44, 44, 44, - 36, 43, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 61, 36, - 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 87, 43, 43, 43, 86, - 86, 86, 86, 85, 87, 43, 43, 43, 43, 43, 2, 88, 2, 66, 70, 44, - 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, - 2, 2, 2,108, 2, 59, 43, 84, 36, 83, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, - 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 61, 43, 85, 86, 87, 85, 86, 44, 44, - 86, 85, 86, 86, 87, 43, 44, 44, 92, 44, 2, 7, 7, 7, 7, 7, - 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, - 7, 7, 7, 7, 7,100, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, - 36, 36, 36, 70, 85, 87, 44, 2, 36, 36, 94, 85, 43, 43, 43, 80, - 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, - 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, - 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, - 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 2, - 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 2, - 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, - 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, - 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, - 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 16, 16, 16, 16, 34,110, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, - 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, - 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, - 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, - 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,111, 40, 40, - 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, - 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, - 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16,112,112,112,112, - 16, 16,110, 16, 11, 11,113,114, 41, 16,110, 16, 11, 11,113, 41, - 16, 16, 44, 16, 11, 11,115, 41, 16, 16, 16, 16, 11, 11,116, 41, - 44, 16,110, 16, 11, 11,113,117,118,118,118,118,118,119, 65, 65, - 120,120,120, 2,121,122,121,122, 2, 2, 2, 2,123, 65, 65,124, - 2, 2, 2, 2,125,126, 2,127,128, 2,129,130, 2, 2, 2, 2, - 2, 9,128, 2, 2, 2, 2,131, 65, 65,132, 65, 65, 65, 65, 65, - 133, 44, 27, 27, 27, 8,129,134, 27, 27, 27, 27, 27, 8,129,104, - 40, 40, 40, 40, 40, 40, 81, 44, 20, 20, 20, 20, 20, 20, 20, 20, - 135, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43,136, 51, - 109, 51,109, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, - 67,137, 67,138, 67, 34, 11, 16, 11, 32,138, 67, 49, 11, 11, 67, - 67, 67,137,137,137, 11, 11,139, 11, 11, 35, 36, 39, 67, 16, 11, - 8, 8, 49, 16, 16, 26, 67,140, 27, 27, 27, 27, 27, 27, 27, 27, - 105,105,105,105,105,105,105,105,105,141,142,105,143, 67, 44, 44, - 8, 8,144, 67, 67, 8, 67, 67,144, 26, 67,144, 67, 67, 67,144, - 67, 67, 67, 67, 67, 67, 67, 8, 67,144,144, 67, 67, 67, 67, 67, - 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, - 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, - 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, - 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, - 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, - 67, 67, 67, 26, 67, 67, 67, 67, 26, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 26, - 67, 67, 67, 67, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, - 27, 27, 67, 67, 67, 67, 67, 67, 8, 8,129,147, 8, 8, 8, 8, - 8, 8, 8, 4, 4, 4, 4, 4, 8,129,148,148,148,148,148,148, - 148,148,148,148,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, - 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,144, 26, 8, 8,144, 67, - 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, - 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, - 32, 32,140, 67, 67,138, 34,149, 43, 32, 44, 44, 93, 2, 99, 2, - 16, 16, 16,150, 44, 44,150, 44, 36, 36, 36, 36, 44, 44, 44, 52, - 64, 44, 44, 44, 44, 44, 44, 57, 36, 36, 36, 61, 44, 44, 44, 44, - 36, 36, 36, 61, 36, 36, 36, 61, 2,121,121, 2,125,126,121, 2, - 2, 2, 2, 6, 2,108,121, 2,121, 4, 4, 4, 4, 2, 2, 88, - 2, 2, 2, 2, 2,120, 2, 2,108,151, 2, 2, 2, 2, 2, 2, - 67, 2,152,148,148,148,153, 44, 67, 67, 67, 67, 67, 55, 67, 67, - 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, - 1, 2,154,155, 4, 4, 4, 4, 4, 67, 4, 4, 4, 4,156,157, - 158,105,105,105,105, 43, 43, 86,159, 40, 40, 67,105,160, 63, 67, - 36, 36, 36, 61, 57,161,162, 69, 36, 36, 36, 36, 36, 63, 40, 69, - 44, 44, 62, 36, 36, 36, 36, 36, 67, 27, 27, 67, 67, 67, 67, 67, - 67, 67, 67, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, 92, - 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, - 163, 27, 27, 27, 27, 27, 27, 27, 36, 36, 83, 36, 36, 36, 36, 36, - 67, 67, 67, 92, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36,164, 2, - 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, - 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, - 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, - 41, 41, 41,162, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, + 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, + 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 43, 44, 16, 10, + 43, 43, 40, 45, 11, 46, 46, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 47, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 48, 34, 32, 34, 11, + 32, 49, 42, 42, 50, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 47, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 46, 51, 2, 2, 2, + 16, 16, 16, 16, 52, 53, 54, 55, 56, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 57, 58, 59, 42, 58, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 61, + 36, 62, 63, 43, 43, 43, 43, 43, 64, 64, 64, 8, 9, 65, 2, 66, + 42, 42, 42, 42, 42, 59, 67, 2, 68, 36, 36, 36, 36, 69, 42, 42, + 7, 7, 7, 7, 7, 2, 2, 36, 70, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 71, 42, 42, 42, 72, 49, 42, 42, 73, 74, 75, 42, 42, 36, + 7, 7, 7, 7, 7, 36, 76, 77, 2, 2, 2, 2, 2, 2, 2, 78, + 69, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 79, 61, 36, + 36, 36, 36, 42, 42, 42, 42, 42, 70, 43, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, + 42, 42, 39, 21, 2, 80, 56, 20, 36, 36, 36, 42, 42, 74, 42, 42, + 42, 42, 74, 42, 74, 42, 42, 43, 2, 2, 2, 2, 2, 2, 2, 63, + 36, 36, 36, 36, 69, 42, 43, 63, 36, 36, 36, 36, 36, 60, 43, 43, + 36, 36, 36, 36, 81, 36, 36, 36, 64, 43, 43, 56, 42, 42, 42, 42, + 36, 36, 36, 36, 82, 42, 42, 42, 42, 83, 42, 42, 42, 42, 42, 42, + 42, 84, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 84, 70, 85, + 86, 42, 42, 42, 84, 85, 86, 85, 69, 42, 42, 42, 36, 36, 36, 36, + 36, 42, 2, 7, 7, 7, 7, 7, 87, 36, 36, 36, 36, 36, 36, 36, + 69, 85, 61, 36, 36, 36, 60, 61, 60, 61, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 60, 36, 36, 36, 60, 60, 43, 36, 36, 43, 70, 85, + 86, 42, 79, 88, 89, 88, 86, 60, 43, 43, 43, 88, 43, 43, 36, 61, + 36, 42, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 55, 62, 79, + 56, 84, 61, 36, 36, 60, 43, 61, 60, 36, 61, 60, 36, 43, 79, 85, + 86, 79, 43, 56, 79, 56, 42, 43, 56, 43, 43, 43, 61, 36, 60, 60, + 43, 43, 43, 7, 7, 7, 7, 7, 42, 36, 69, 63, 43, 43, 43, 43, + 56, 84, 61, 36, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, + 60, 36, 61, 36, 36, 43, 70, 85, 86, 42, 42, 56, 84, 88, 86, 43, + 60, 43, 43, 43, 43, 43, 43, 43, 65, 43, 43, 43, 61, 42, 42, 42, + 56, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 43, 70, 86, + 86, 42, 79, 88, 89, 88, 86, 43, 43, 43, 56, 84, 43, 43, 36, 61, + 77, 27, 27, 27, 43, 43, 43, 43, 43, 70, 61, 36, 36, 60, 43, 36, + 60, 36, 36, 43, 61, 60, 60, 36, 43, 61, 60, 43, 36, 60, 43, 36, + 36, 36, 36, 36, 36, 43, 43, 85, 84, 89, 43, 85, 89, 85, 86, 43, + 60, 43, 43, 88, 43, 43, 43, 43, 27, 90, 66, 66, 55, 91, 43, 43, + 84, 85, 70, 36, 36, 36, 60, 36, 60, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 43, 70, 42, 84, 85, 89, 42, 79, 42, 42, 43, + 43, 43, 56, 79, 36, 60, 36, 43, 43, 43, 43, 92, 27, 27, 27, 90, + 69, 85, 71, 36, 36, 36, 60, 36, 36, 36, 61, 36, 36, 43, 70, 86, + 85, 85, 89, 84, 89, 85, 42, 43, 43, 43, 88, 89, 43, 43, 36, 60, + 61, 93, 43, 43, 43, 43, 43, 43, 42, 85, 36, 36, 36, 36, 60, 36, + 36, 36, 36, 36, 36, 69, 70, 85, 86, 42, 79, 85, 89, 85, 86, 76, + 43, 43, 36, 93, 27, 27, 27, 94, 27, 27, 27, 27, 90, 36, 36, 36, + 56, 85, 61, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 36, 36, 36, + 36, 61, 36, 36, 36, 36, 61, 43, 36, 36, 36, 60, 43, 79, 43, 88, + 85, 42, 79, 79, 85, 85, 85, 85, 43, 85, 63, 43, 43, 43, 43, 43, + 61, 36, 36, 36, 36, 36, 36, 36, 69, 36, 42, 42, 42, 79, 43, 95, + 36, 36, 36, 74, 42, 42, 42, 59, 7, 7, 7, 7, 7, 2, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 61, 60, 60, 36, 36, 60, 36, 36, + 36, 36, 61, 61, 36, 36, 36, 36, 69, 36, 42, 42, 42, 42, 70, 43, + 36, 36, 60, 80, 42, 42, 42, 79, 7, 7, 7, 7, 7, 43, 36, 36, + 76, 66, 2, 2, 2, 2, 2, 2, 2, 96, 96, 66, 42, 66, 66, 66, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 49, 49, 49, 4, 4, 85, + 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, + 56, 42, 42, 42, 42, 42, 42, 84, 42, 42, 59, 42, 36, 36, 69, 42, + 42, 42, 42, 42, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 79, 66, + 66, 66, 66, 75, 66, 66, 91, 66, 2, 2, 96, 66, 21, 63, 43, 43, + 36, 36, 36, 36, 36, 93, 86, 42, 84, 42, 42, 42, 86, 84, 86, 70, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 85, 42, 36, 36, 42, + 70, 85, 97, 93, 85, 85, 85, 36, 69, 42, 70, 36, 36, 36, 36, 36, + 36, 84, 86, 84, 85, 85, 86, 93, 7, 7, 7, 7, 7, 85, 86, 66, + 11, 11, 11, 47, 43, 43, 47, 43, 16, 16, 16, 16, 16, 52, 44, 16, + 36, 36, 36, 36, 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, + 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, 36, 36, 36, 36, + 36, 36, 36, 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 56, 42, + 2, 2, 2, 2, 98, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, + 66, 66, 66, 66, 66, 43, 43, 43, 11, 11, 11, 43, 16, 16, 16, 43, + 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 71, + 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,102,103, 43, + 36, 36, 36, 36, 36, 62, 2,104,105, 36, 36, 36, 60, 43, 43, 43, + 36, 42, 84, 43, 43, 43, 43, 61, 36, 42,106, 63, 43, 43, 43, 43, + 36, 42, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 60, 36, + 60, 42, 43, 43, 43, 43, 43, 43, 36, 36, 42, 86, 42, 42, 42, 85, + 85, 85, 85, 84, 86, 42, 42, 42, 42, 42, 2, 87, 2, 65, 69, 43, + 7, 7, 7, 7, 7, 43, 43, 43, 27, 27, 27, 27, 27, 43, 43, 43, + 2, 2, 2,107, 2, 58, 42, 83, 36, 82, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 60, 43, 43, 43, 36, 36, 69, 70, 36, 36, 36, 36, + 36, 36, 36, 36, 69, 60, 43, 43, 36, 36, 36, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 60, 42, 84, 85, 86, 84, 85, 43, 43, + 85, 84, 85, 85, 86, 42, 43, 43, 91, 43, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 43, 36, 36, 60, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 43, 43, 36, 36, 36, 36, 36, 43, 43, 43, + 7, 7, 7, 7, 7, 99, 43, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 36, 36, 36, 69, 84, 86, 43, 2, 36, 36, 93, 84, 42, 42, 42, 79, + 84, 84, 86, 42, 42, 42, 84, 85, 85, 86, 42, 42, 42, 42, 79, 56, + 2, 2, 2, 87, 2, 2, 2, 43, 42, 42, 42, 42, 42, 42, 42,108, + 42, 42, 42, 42, 42, 42, 42, 43, 42, 42, 42, 42, 42, 42, 43, 43, + 42, 42, 97, 36, 36, 36, 36, 36, 36, 36, 84, 42, 42, 84, 84, 85, + 85, 84, 97, 36, 36, 36, 60, 2, 96, 66, 66, 66, 66, 49, 42, 42, + 42, 42, 66, 66, 66, 66, 21, 2, 42, 97, 36, 36, 36, 36, 36, 36, + 93, 42, 42, 85, 42, 86, 42, 36, 36, 36, 36, 84, 42, 85, 86, 86, + 42, 85, 43, 43, 43, 43, 2, 2, 36, 36, 85, 85, 85, 85, 42, 42, + 42, 42, 85, 42, 43, 92, 2, 2, 7, 7, 7, 7, 7, 43, 61, 36, + 36, 36, 36, 36, 39, 39, 39, 2, 16, 16, 16, 16, 34,109, 43, 43, + 11, 11, 11, 11, 11, 46, 47, 11, 2, 2, 2, 2, 43, 43, 43, 43, + 42, 59, 42, 42, 42, 42, 42, 42, 84, 42, 42, 42, 70, 36, 69, 36, + 36, 36, 70, 93, 42, 60, 43, 43, 16, 16, 16, 16, 16, 16, 39, 39, + 39, 39, 39, 39, 39, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, + 16, 16, 16, 16, 16,110, 39, 39, 32, 32, 32, 16, 16, 16, 16, 32, + 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 43, 11, 11, 11, 43, + 16, 16, 16, 16, 47, 47, 47, 47, 16, 16, 16, 16, 16, 16, 16, 43, + 16, 16, 16, 16,111,111,111,111, 16, 16,109, 16, 11, 11,112,113, + 40, 16,109, 16, 11, 11,112, 40, 16, 16, 43, 16, 11, 11,114, 40, + 16, 16, 16, 16, 11, 11,115, 40, 43, 16,109, 16, 11, 11,112,116, + 117,117,117,117,117,118, 64, 64,119,119,119, 2,120,121,120,121, + 2, 2, 2, 2,122, 64, 64,123, 2, 2, 2, 2,124,125, 2,126, + 127, 2,128,129, 2, 2, 2, 2, 2, 9,127, 2, 2, 2, 2,130, + 64, 64,131, 64, 64, 64, 64, 64,132, 43, 27, 27, 27, 8,128,133, + 27, 27, 27, 27, 27, 8,128,103, 39, 39, 39, 39, 39, 39, 80, 43, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43, 43, + 42, 42, 42, 42, 42, 42,134, 50,108, 50,108, 42, 42, 42, 42, 42, + 79, 43, 43, 43, 43, 43, 43, 43, 66,135, 66,136, 66, 34, 11, 16, + 11, 32,136, 66, 48, 11, 11, 66, 66, 66,135,135,135, 11, 11,137, + 11, 11, 35, 36,138, 66, 16, 11, 8, 8, 48, 16, 16, 26, 66,139, + 27, 27, 27, 27, 27, 27, 27, 27,104,104,104,104,104,104,104,104, + 104,140,141,104,142, 66, 43, 43, 8, 8,143, 66, 66, 8, 66, 66, + 143, 26, 66,143, 66, 66, 66,143, 66, 66, 66, 66, 66, 66, 66, 8, + 66,143,143, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 66, 66, 66, 66, 4, 4, 66, 66, + 8, 66, 66, 66,144,145, 66, 66, 66, 66, 66, 66, 66, 66,143, 66, + 66, 66, 66, 66, 66, 26, 8, 8, 8, 8, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 8, 8, 8, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 91, 43, 43, 27, 27, 27, 27, 27, 27, 66, 66, + 66, 66, 66, 66, 66, 27, 27, 27, 66, 66, 66, 26, 66, 66, 66, 66, + 26, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, + 66, 66, 66, 66, 66, 66, 66, 26, 66, 66, 66, 66, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, + 8, 8,128,146, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,128,147,147,147,147,147,147,147,147,147,147,146, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,143, 26, 8, 8,143, 66, 66, 66, 43, 66, 66, 66, 66, 66, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 39, 11, + 32, 32,139, 66, 66,136, 34,148, 42, 32, 43, 43, 92, 2, 98, 2, + 16, 16, 16,149, 43, 43,149, 43, 36, 36, 36, 36, 43, 43, 43, 51, + 63, 43, 43, 43, 43, 43, 43, 56, 36, 36, 36, 60, 43, 43, 43, 43, + 36, 36, 36, 60, 36, 36, 36, 60, 2,120,120, 2,124,125,120, 2, + 2, 2, 2, 6, 2,107,120, 2,120, 4, 4, 4, 4, 2, 2, 87, + 2, 2, 2, 2, 2,119, 2, 2,107,150, 2, 2, 2, 2, 2, 2, + 66, 2,151,147,147,147,152, 43, 66, 66, 66, 66, 66, 54, 66, 66, + 66, 66, 43, 43, 43, 43, 43, 43, 66, 66, 66, 43, 43, 43, 43, 43, + 1, 2,153,154, 4, 4, 4, 4, 4, 66, 4, 4, 4, 4,155,156, + 157,104,104,104,104, 42, 42, 85,158, 39, 39, 66,104,159, 62, 66, + 36, 36, 36, 60, 56,160,161, 68, 36, 36, 36, 36, 36, 62, 39, 68, + 43, 43, 61, 36, 36, 36, 36, 36, 66, 27, 27, 66, 66, 66, 66, 66, + 66, 66, 66, 43, 43, 43, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, + 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, + 162, 27, 27, 27, 27, 27, 27, 27, 36, 36, 82, 36, 36, 36, 36, 36, + 66, 66, 66, 91, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 2, + 7, 7, 7, 7, 7, 36, 43, 43, 32, 32, 32, 32, 32, 32, 32, 69, + 50,164, 42, 42, 42, 42, 42, 87, 32, 32, 32, 32, 32, 32, 39, 42, + 36, 36, 36,104,104,104,104,104, 42, 2, 2, 2, 43, 43, 43, 43, + 40, 40, 40,161, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 41,165, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 44, 32,150,150, 32, 32, 32, 47, 44, - 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, - 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, - 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 86, 86, 86, 86, 86, 86, 86, 86, 43, 44, 44, 44, 44, 2, - 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, - 36, 36, 36, 70, 43, 43, 43, 43, 43, 86, 44, 44, 44, 44, 44, 93, - 36, 70, 86, 43, 43, 86, 43, 86,107, 2, 2, 2, 2, 2, 2, 52, - 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, - 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 85, - 87, 85, 87, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 85, 44, - 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 94, 85, 36, - 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 62, 83, 2, 36, 36, 36, 36, 36, 94, 43, 86, - 2, 83,169, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, - 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,114, 40, 40, - 16, 16, 16, 16,111, 41, 44, 44, 36, 94, 87, 86, 85,107, 87, 44, - 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, - 170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171, - 16, 16, 16,110, 44, 44, 44, 44, 44,150, 16, 16, 44, 44, 62, 71, - 36, 36, 36, 36,172, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, - 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, - 41,117, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36,148, 44, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 44, 55, 36, 36, 36, 36, 36, 36,168, 67, - 2, 2, 2,152,130, 44, 44, 44, 6,173,174,148,148,148,148,148, - 148,148,130,152,130, 2,127,175, 2, 64, 2, 2,156,148,148,130, - 2,176, 8,177, 66, 2, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 61, 79, 93, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,129,130, 4, 2, 36, 36, 36, 36, 36, - 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, - 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, - 20,178, 56,135, 26, 8,144, 92, 44, 44, 44, 44, 79, 65, 67, 44, - 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, - 2, 64, 44,179, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, - 105,105,143, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 92, - 67, 67, 67, 67, 67, 67, 92, 44, 92, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 67, 50, 44,180, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, - 155, 36, 36, 36, 36,181, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, - 36, 36, 36, 36, 36, 36, 36, 93, 36, 36, 44, 44, 36, 36, 36, 36, - 182,105,105, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, - 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, - 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, 44, 93, - 11, 11, 11, 11, 11, 47, 11, 11, 11, 47, 11,150, 16, 16, 16, 16, - 16,150, 16, 16, 16, 16, 16, 16, 16,150, 16, 16, 16,150,110, 44, - 40, 40, 40, 52, 40, 40, 40, 40, 81, 40, 40, 40, 40, 81, 44, 44, - 36, 36, 36, 44, 61, 36, 36, 36, 36, 36, 36, 62, 61, 44, 61, 62, - 36, 36, 36, 93, 27, 27, 27, 27, 36, 36, 36, 77,163, 27, 27, 27, - 44, 44, 44,179, 27, 27, 27, 27, 36, 61, 36, 44, 44,179, 27, 27, - 36, 36, 36, 27, 27, 27, 44, 93, 36, 36, 36, 36, 36, 44, 44, 93, - 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, - 70, 43, 57, 80, 44, 44, 43, 43, 36, 36, 62, 36, 62, 36, 36, 36, - 36, 36, 36, 44, 43, 80, 44, 57, 27, 27, 27, 27,100, 44, 44, 44, - 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36,183, 30, - 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 78, 36, 36, 36, - 36, 36, 70, 80, 44,179, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, - 36, 36, 36, 44, 93, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, - 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, - 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, - 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, - 7, 7, 7, 7, 7, 36, 36, 69, 11, 11, 11, 44, 57, 43, 43,159, - 16, 16, 16, 44, 44, 44, 44, 8, 27, 27, 27, 27, 27, 27, 27,100, - 36, 36, 36, 36, 36, 57,184, 44, 36, 44, 44, 44, 44, 44, 44, 44, - 44, 36, 61, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 43, 43, - 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, - 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, - 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, - 43, 43, 43, 60, 2, 2, 2, 44, 27, 27, 27, 7, 7, 7, 7, 7, - 71, 70, 71, 44, 44, 44, 44, 57, 86, 87, 43, 85, 87, 60,185, 2, - 2, 80, 44, 44, 44, 44, 79, 44, 43, 71, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 70, 43, 43, 87, 43, 43, 43, 80, 7, 7, 7, 7, 7, - 2, 2, 94, 98, 44, 44, 44, 44, 36, 70, 2, 61, 44, 44, 44, 44, - 36, 94, 86, 43, 43, 43, 43, 85, 98, 36, 63, 2, 59, 43, 60, 87, - 7, 7, 7, 7, 7, 63, 63, 2,179, 27, 27, 27, 27, 27, 27, 27, - 27, 27,100, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 86, 87, - 43, 86, 85, 43, 2, 2, 2, 71, 70, 44, 44, 44, 44, 44, 44, 44, - 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, - 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, - 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, - 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, - 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, - 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 62, 44, 61, - 36, 36, 36, 62, 86, 87, 43, 43, 80, 90, 89, 89, 86, 90, 86, 85, - 71, 71, 2, 93, 64, 44, 44, 44, 57, 80, 44, 44, 44, 44, 44, 44, - 36, 36, 94, 86, 43, 43, 43, 43, 86, 43, 85, 71, 36, 63, 2, 2, - 7, 7, 7, 7, 7, 2, 93, 71, 86, 87, 43, 43, 85, 85, 86, 87, - 85, 43, 36, 72, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 94, - 86, 43, 43, 44, 86, 86, 43, 87, 60, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 43, 44, 86, 87, 43, 43, 43, 85, 87, 87, - 60, 2, 61, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 2, 64, 44, - 36, 36, 36, 36, 36, 70, 87, 86, 43, 43, 43, 87, 63, 44, 44, 44, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, - 36, 36, 36, 36, 36, 61, 57, 87, 86, 43, 43, 87, 43, 43, 44, 44, - 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, - 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, - 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, - 98, 87, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, - 94, 86, 43, 43, 44, 43, 86, 86, 71, 72, 90, 44, 44, 44, 44, 44, - 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 85, 70, 43, 60, - 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, - 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, - 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, - 63, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 87, - 63, 2, 2, 44, 44, 44, 44, 44, 2, 36, 36, 36, 36, 36, 36, 36, - 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 89, 43, 43, 43, - 85, 43, 87, 80, 44, 44, 44, 44, 36, 36, 36, 61, 36, 62, 36, 36, - 70, 43, 43, 80, 44, 80, 43, 57, 43, 43, 43, 70, 44, 44, 44, 44, - 36, 36, 36, 62, 61, 36, 36, 36, 36, 36, 36, 36, 36, 86, 86, 90, - 43, 89, 87, 87, 61, 44, 44, 44, 36, 70, 85,107, 64, 44, 44, 44, - 43, 94, 36, 36, 36, 36, 36, 36, 36, 36, 86, 43, 43, 80, 44, 86, - 85, 60, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 80, 44, 44, - 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, - 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, - 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, - 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, - 43, 43, 43, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 43, - 43, 43, 43, 43, 43, 86, 87, 43, 43, 43, 60, 44, 44, 44, 44, 44, - 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, - 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 44, 44, 62, 36, 40, 69, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 83,164, 2, 27, 27, 27, 30, 2, 64, 44, 44, - 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, - 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, - 86, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 62, - 40, 40, 52, 40, 40, 40, 52, 81, 36, 61, 44, 44, 44, 44, 44, 44, - 44, 61, 44, 44, 44, 44, 44, 44, 36, 61, 62, 44, 44, 44, 44, 44, - 44, 44, 36, 36, 44, 44, 44, 44, 36, 36, 36, 36, 36, 44, 50, 60, - 65, 65, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 43, 44, - 43, 43, 43, 80, 44, 44, 44, 44, 67, 67, 67, 92, 55, 67, 67, 67, - 67, 67,186, 87, 43, 67,186, 86, 86,187, 65, 65, 65, 84, 43, 43, - 43, 76, 50, 43, 43, 43, 67, 67, 67, 67, 67, 67, 67, 43, 43, 67, - 67, 43, 76, 44, 44, 44, 44, 44, 27, 27, 44, 44, 44, 44, 44, 44, - 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 16, 16, 16,110, 16, 16, 16, 16, 16, - 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 11, - 44, 47, 48, 47, 48, 11, 47, 11, 11, 11, 11, 16, 16,150,150, 16, - 16, 16,150, 16, 16, 16, 16, 16, 16, 16, 11, 48, 11, 47, 48, 11, - 11, 11, 47, 11, 11, 11, 47, 16, 16, 16, 16, 16, 11, 48, 11, 47, - 11, 11, 47, 47, 44, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, - 16, 16, 16, 44, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, - 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, - 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, - 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, - 16, 33, 16, 16, 16, 32, 44, 7, 43, 43, 43, 76, 67, 50, 43, 43, - 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, - 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, - 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, - 44, 44,150, 16, 16,110, 44, 44, 43, 43, 43, 80, 43, 43, 43, 43, - 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, - 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 57, - 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, - 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, - 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, - 188, 7, 7, 7, 7,189, 44, 93, 36, 36, 36, 61, 36, 36, 62, 61, - 36, 36, 61,179, 27, 27, 27, 27, 16, 16, 43, 43, 43, 74, 44, 44, - 27, 27, 27, 27, 27, 27,163, 27,190, 27,100, 44, 44, 44, 44, 44, - 27, 27, 27, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, 44, - 36, 36, 62, 36, 36, 36, 36, 36, 62, 61, 61, 62, 62, 36, 36, 36, - 36, 61, 36, 36, 62, 62, 44, 44, 44, 61, 44, 62, 62, 62, 62, 36, - 62, 61, 61, 62, 62, 62, 62, 62, 62, 61, 61, 62, 36, 61, 36, 36, - 36, 61, 36, 36, 62, 36, 61, 61, 36, 36, 36, 36, 36, 62, 36, 36, - 62, 36, 62, 36, 36, 62, 36, 36, 8, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, - 27, 27, 27, 27, 27, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 44, - 44, 44, 44, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 92, 44, 44, 44, 67, 44, 44, 44, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 25, 41, 41, 67, 67, 67, 67, 44, 44, 67, 67, - 67, 67, 67, 92, 44, 55, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, - 67, 67, 67, 67, 67, 44, 44, 55, 67, 67, 67, 92, 44, 44, 44, 67, - 67, 67, 67, 67, 67, 67, 92, 55, 67, 92, 67, 67, 67, 67, 67, 67, - 79, 44, 44, 44, 44, 44, 44, 44,171,171,171,171,171,171,171, 44, - 171,171,171,171,171,171,171, 0, 0, 0, 29, 21, 21, 21, 23, 21, - 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, - 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, - 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, - 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, - 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, - 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, 25, 9, 26, 12, 11, 11, - 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, 2, 12, 17, 12, 21, 12, - 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, 1, 1, 21, 23, 26, 26, - 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, - 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, 6, 2, 24, 7, 7, 6, - 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 2, 10, 10, 2, 15, 26, - 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, 21, 26, 10, 7, 21, 15, - 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, 14, 14, 14, 7, 10, 21, - 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, 8, 24, 5, 24, 2, 24, - 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, 20, 19, 22, 20, 27, 28, - 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, - 1, 2, 15, 6, 18, 6, 23, 2, 12, 11, 9, 26, 26, 9, 26, 5, - 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, - 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, - 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, - 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, - 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 25, 2, 25, 24, 2, 15, - 12, 15, 14, 2, 21, 14, 7, 15, 12, 17, 21, 1, 26, 10, 10, 1, - 7, 13, 13, 2, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, + 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 46, 43, + 51, 39,166, 35, 39, 35, 36, 36, 36, 70, 36, 70, 36, 69, 36, 36, + 36, 93, 86, 84, 66, 66, 79, 43, 27, 27, 27, 66,167, 43, 43, 43, + 36, 36, 2, 2, 43, 43, 43, 43, 85, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 85, 85, 85, 85, 85, 85, 85, 85, 42, 43, 43, 43, 43, 2, + 42, 36, 36, 36, 2, 71, 71, 69, 36, 36, 36, 42, 42, 42, 42, 2, + 36, 36, 36, 69, 42, 42, 42, 42, 42, 85, 43, 43, 43, 43, 43, 92, + 36, 69, 85, 42, 42, 85, 42, 85,106, 2, 2, 2, 2, 2, 2, 51, + 7, 7, 7, 7, 7, 43, 43, 2, 36, 36, 69, 68, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 60, 36, 36, 36, 36, 69, 42, 42, 84, + 86, 84, 86, 79, 43, 43, 43, 43, 36, 69, 36, 36, 36, 36, 84, 43, + 7, 7, 7, 7, 7, 43, 2, 2, 68, 36, 36, 76, 66, 93, 84, 36, + 70, 42, 70, 69, 70, 36, 36, 42, 69, 60, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 61, 82, 2, 36, 36, 36, 36, 36, 93, 42, 85, + 2, 82,168, 79, 43, 43, 43, 43, 61, 36, 36, 60, 61, 36, 36, 60, + 61, 36, 36, 60, 43, 43, 43, 43, 16, 16, 16, 16, 16,113, 39, 39, + 16, 16, 16, 16,110, 40, 43, 43, 36, 93, 86, 85, 84,106, 86, 43, + 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 43, 61, 36, 36, + 169,169,169,169,169,169,169,169,170,170,170,170,170,170,170,170, + 16, 16, 16,109, 43, 43, 43, 43, 43,149, 16, 16, 43, 43, 61, 70, + 36, 36, 36, 36,171, 36, 36, 36, 36, 36, 36, 60, 36, 36, 60, 60, + 36, 61, 60, 36, 36, 36, 36, 36, 36, 40, 40, 40, 40, 40, 40, 40, + 40, 22, 66, 66, 66, 66, 66, 66, 66, 77, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,147, 66, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 66, 66, 66, 66, 36, 36, 36, 36, 36, 36,167, 66, + 2, 2, 2,151,129, 43, 43, 43, 6,172,173,147,147,147,147,147, + 147,147,129,151,129, 2,126,174, 2, 63, 2, 2,155,147,147,129, + 2,175, 8,176, 65, 2, 43, 43, 36, 36, 60, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 60, 78, 92, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,128,129, 4, 2, 36, 36, 36, 36, 36, + 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, + 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 60, 43, + 20,177, 55,178, 26, 8,143, 91, 43, 43, 43, 43, 78, 64, 66, 43, + 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 60, 36, 61, + 2, 63, 43,179, 27, 27, 27, 27, 27, 27, 43, 54, 66, 66, 66, 66, + 104,104,142, 27, 90, 66, 66, 66, 66, 66, 66, 66, 66, 27, 66, 91, + 66, 66, 66, 66, 66, 66, 91, 43, 91, 43, 43, 43, 43, 43, 43, 43, + 66, 66, 66, 66, 66, 66, 49, 43,180, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 43, 43, 27, 27, 43, 43, 43, 43, 61, 36, + 154, 36, 36, 36, 36,181, 43, 43, 36, 36, 36, 42, 42, 79, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 92, 36, 36, 43, 43, 36, 36, 36, 36, + 182,104,104, 43, 43, 43, 43, 43, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 43, 43, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 43, 43, + 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, 43, 92, + 11, 11, 11, 11, 11, 46, 11, 11, 11, 46, 11,149, 16, 16, 16, 16, + 16,149, 16, 16, 16, 16, 16, 16, 16,149, 16, 16, 16,149,109, 43, + 39, 39, 39, 51, 39, 39, 39, 39, 80, 39, 39, 39, 39, 80, 43, 43, + 36, 36, 36, 43, 60, 36, 36, 36, 36, 36, 36, 61, 60, 43, 60, 61, + 36, 36, 36, 92, 27, 27, 27, 27, 36, 36, 36, 76,162, 27, 27, 27, + 43, 43, 43,179, 27, 27, 27, 27, 36, 60, 36, 43, 43,179, 27, 27, + 36, 36, 36, 27, 27, 27, 43, 92, 36, 36, 36, 36, 36, 43, 43, 92, + 36, 36, 36, 36, 43, 43, 27, 36, 43, 27, 27, 27, 27, 27, 27, 27, + 69, 42, 56, 79, 43, 43, 42, 42, 36, 36, 61, 36, 61, 36, 36, 36, + 36, 36, 36, 43, 42, 79, 43, 56, 27, 27, 27, 27, 99, 43, 43, 43, + 2, 2, 2, 2, 63, 43, 43, 43, 36, 36, 36, 36, 36, 36,183, 30, + 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 77, 36, 36, 36, + 36, 36, 69, 79, 43,179, 27, 27, 2, 2, 2, 63, 43, 43, 43, 43, + 36, 36, 36, 43, 92, 2, 2, 2, 36, 36, 36, 43, 27, 27, 27, 27, + 36, 60, 43, 43, 27, 27, 27, 27, 36, 43, 43, 43, 92, 2, 63, 43, + 43, 43, 43, 43,179, 27, 27, 27, 11, 46, 43, 43, 43, 43, 43, 43, + 16,109, 43, 43, 43, 27, 27, 27, 36, 36, 42, 42, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 36, 36, 68, 11, 11, 11, 43, 56, 42, 42,158, + 16, 16, 16, 43, 43, 43, 43, 8, 27, 27, 27, 27, 27, 27, 27, 99, + 36, 36, 36, 36, 36, 56,184, 43, 36, 43, 43, 43, 43, 43, 43, 43, + 43, 36, 82, 36, 43, 43, 43, 43, 96, 66, 66, 66, 91, 43, 43, 43, + 43, 43, 43, 43, 43, 42, 42, 42, 27, 27, 27, 94, 43, 43, 43, 43, + 180, 27, 30, 2, 2, 43, 43, 43, 36, 42, 42, 2, 2, 43, 43, 43, + 36, 36,183, 27, 27, 27, 43, 43, 86, 97, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 42, 59, 2, 2, 2, 43, + 27, 27, 27, 7, 7, 7, 7, 7, 70, 69, 70, 43, 43, 43, 43, 56, + 85, 86, 42, 84, 86, 59,185, 2, 2, 79, 43, 43, 43, 43, 78, 43, + 42, 70, 36, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, 86, 42, + 42, 42, 79, 7, 7, 7, 7, 7, 2, 2, 93, 97, 43, 43, 43, 43, + 36, 69, 2, 60, 43, 43, 43, 43, 36, 93, 85, 42, 42, 42, 42, 84, + 97, 36, 62, 2, 58, 42, 59, 86, 7, 7, 7, 7, 7, 62, 62, 2, + 179, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 85, 86, 42, 85, 84, 42, 2, 2, 2, 70, + 69, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 60, 36, 36, 61, + 36, 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 62, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 69, 85, 86, 42, 42, 42, 79, 43, 43, + 42, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 56, 70, 85, + 84, 85, 89, 88, 89, 88, 85, 43, 60, 43, 43, 88, 43, 43, 61, 36, + 36, 85, 43, 42, 42, 42, 79, 43, 42, 42, 79, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 61, 43, 60, 36, 36, 36, 61, 85, 86, 42, 42, + 79, 89, 88, 88, 85, 89, 85, 84, 70, 70, 2, 92, 63, 43, 43, 43, + 56, 79, 43, 43, 43, 43, 43, 43, 36, 36, 93, 85, 42, 42, 42, 42, + 85, 42, 84, 70, 36, 62, 2, 2, 7, 7, 7, 7, 7, 2, 92, 70, + 85, 86, 42, 42, 84, 84, 85, 86, 84, 42, 36, 71, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 93, 85, 42, 42, 43, 85, 85, 42, 86, + 59, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 43, + 85, 86, 42, 42, 42, 84, 86, 86, 59, 2, 60, 43, 43, 43, 43, 43, + 2, 2, 2, 2, 2, 2, 63, 43, 36, 36, 36, 36, 36, 69, 86, 85, + 42, 42, 42, 86, 62, 43, 43, 43, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 56, 86, + 85, 42, 42, 86, 42, 42, 43, 43, 7, 7, 7, 7, 7, 27, 2, 96, + 42, 42, 42, 42, 86, 59, 43, 43, 27, 99, 43, 43, 43, 43, 43, 61, + 36, 36, 36, 60, 61, 43, 36, 36, 36, 36, 61, 60, 36, 36, 36, 36, + 85, 85, 85, 88, 89, 56, 84, 70, 97, 86, 2, 63, 43, 43, 43, 43, + 36, 36, 36, 36, 43, 36, 36, 36, 93, 85, 42, 42, 43, 42, 85, 85, + 70, 71, 89, 43, 43, 43, 43, 43, 69, 42, 42, 42, 42, 70, 36, 36, + 36, 69, 42, 42, 84, 69, 42, 59, 2, 2, 2, 58, 43, 43, 43, 43, + 69, 42, 42, 84, 86, 42, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, + 42, 42, 42, 84, 42, 2, 71, 2, 2, 63, 43, 43, 43, 43, 43, 43, + 2, 2, 2, 2, 2, 43, 43, 43, 84, 42, 84, 84, 43, 43, 43, 43, + 62, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 42, 42, 42, 86, + 62, 2, 2, 43, 43, 43, 43, 43, 2, 36, 36, 36, 36, 36, 36, 36, + 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 88, 42, 42, 42, + 84, 42, 86, 79, 43, 43, 43, 43, 36, 36, 36, 60, 36, 61, 36, 36, + 69, 42, 42, 79, 43, 79, 42, 56, 42, 42, 42, 69, 43, 43, 43, 43, + 36, 36, 36, 61, 60, 36, 36, 36, 36, 36, 36, 36, 36, 85, 85, 89, + 42, 88, 86, 86, 60, 43, 43, 43, 36, 36, 36, 36, 82, 36, 43, 43, + 36, 69, 84,106, 63, 43, 43, 43, 42, 93, 36, 36, 36, 36, 36, 36, + 36, 36, 85, 42, 42, 79, 43, 85, 84, 59, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 79, 43, 43, 27, 27, 90, 66, 66, 66, 55, 20, + 167, 66, 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 43, 43, 92, + 104,104,104,104,104,104,104,181, 2, 2, 63, 43, 43, 43, 43, 43, + 62, 63, 43, 43, 43, 43, 43, 43, 64, 64, 64, 64, 64, 64, 64, 64, + 70, 36, 36, 69, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 85, 86, 42, + 42, 42, 59, 43, 43, 43, 43, 43, 42, 42, 42, 59, 2, 2, 66, 66, + 39, 39, 96, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7,179, 27, 27, + 27, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 61, 36, + 39, 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82,163, 2, + 27, 27, 27, 30, 2, 63, 43, 43, 11, 11, 11, 11, 46,149, 16, 16, + 16, 16, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 43, 56, + 93, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 43, 43, 43, 56, 42, 73, 39, 39, 39, 39, 39, 39, + 39, 87, 79, 43, 43, 43, 43, 43, 85, 39,104,181, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 61, 36, 60, 43, 43, 43, 43, 43, 43, + 39, 39, 51, 39, 39, 39, 51, 80, 43, 60, 43, 43, 43, 43, 43, 43, + 36, 60, 61, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 43, 49, 59, 64, 64, 43, 43, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 66, 91, 43, 66, 66, 43, 43, 43, 66, 66, 66, + 176, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 43, 43, 43, 43, + 66, 66, 66, 91, 54, 66, 66, 66, 66, 66,186, 86, 42, 66,186, 85, + 85,187, 64, 64, 64, 83, 42, 42, 42, 75, 49, 42, 42, 42, 66, 66, + 66, 66, 66, 66, 66, 42, 42, 66, 66, 42, 75, 43, 43, 43, 43, 43, + 27, 27, 43, 43, 43, 43, 43, 43, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16,109, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 46, 11, 43, 46, 47, 46, 47, 11, 46, 11, + 11, 11, 11, 16, 16,149,149, 16, 16, 16,149, 16, 16, 16, 16, 16, + 16, 16, 11, 47, 11, 46, 47, 11, 11, 11, 46, 11, 11, 11, 46, 16, + 16, 16, 16, 16, 11, 47, 11, 46, 11, 11, 46, 46, 43, 11, 11, 11, + 46, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 43, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 43, 7, + 42, 42, 42, 75, 66, 49, 42, 42, 42, 42, 42, 42, 42, 42, 75, 66, + 66, 66, 49, 66, 66, 66, 66, 66, 66, 66, 75, 21, 2, 2, 43, 43, + 43, 43, 43, 43, 43, 56, 42, 42, 16, 16, 16, 16, 16,138, 16, 16, + 16, 16, 16, 16, 16, 16, 16,109, 43, 43,149, 16, 16,109, 43, 43, + 42, 42, 42, 79, 42, 42, 42, 42, 42, 42, 42, 42, 79, 56, 42, 42, + 42, 56, 79, 42, 42, 79, 43, 43, 39, 39, 39, 39, 39, 39, 39, 43, + 43, 43, 43, 43, 43, 43, 43, 56, 42, 42, 42, 73, 39, 39, 39, 43, + 7, 7, 7, 7, 7, 43, 43, 76, 36, 36, 36, 36, 36, 36, 36, 79, + 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 43, 43, 95, + 36, 36, 36, 36, 36, 82, 42, 42,188, 7, 7, 7, 7,189, 43, 92, + 36, 69, 36, 70, 36, 36, 36, 42, 36, 36, 69, 43, 43, 43, 43, 82, + 36, 36, 36, 60, 36, 36, 61, 60, 36, 36, 60,179, 27, 27, 27, 27, + 16, 16, 42, 42, 42, 73, 43, 43, 27, 27, 27, 27, 27, 27,162, 27, + 190, 27, 99, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 27, 27,162, + 27, 27, 27, 27, 27, 27, 27, 43, 36, 36, 61, 36, 36, 36, 36, 36, + 61, 60, 60, 61, 61, 36, 36, 36, 36, 60, 36, 36, 61, 61, 43, 43, + 43, 60, 43, 61, 61, 61, 61, 36, 61, 60, 60, 61, 61, 61, 61, 61, + 61, 60, 60, 61, 36, 60, 36, 36, 36, 60, 36, 36, 61, 36, 60, 60, + 36, 36, 36, 36, 36, 61, 36, 36, 61, 36, 61, 36, 36, 61, 36, 36, + 8, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 66, 43, 43, + 54, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, 27, 27, 90, 66, + 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 66, 66, 66, 66, 66, + 66, 91, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 91, 43, 43, 43, + 66, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 25, 40, 40, + 66, 66, 66, 66, 91, 43, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, + 8, 8, 8, 8,176, 43, 43, 43, 66, 66, 66, 66, 66, 91, 43, 66, + 66, 66, 66, 91, 91, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, 54, + 66, 66, 66, 66, 66, 91, 43, 54, 66, 91, 66, 66, 66, 66, 66, 66, + 7, 7, 7, 7, 7, 91, 43, 43, 78, 43, 43, 43, 43, 43, 43, 43, + 170,170,170,170,170,170,170, 43,170,170,170,170,170,170,170, 0, + 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, + 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, + 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, + 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, + 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, + 24, 24, 6, 24, 12, 12, 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, + 25, 9, 26, 12, 11, 11, 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, + 2, 12, 17, 12, 21, 12, 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, + 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, + 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, + 6, 2, 24, 7, 7, 6, 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, + 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, + 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, + 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, + 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, + 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, + 25, 22, 18, 21, 21, 29, 1, 2, 15, 6, 18, 6, 12, 11, 9, 26, + 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, + 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, + 18, 17, 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, + 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, + 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, + 25, 2, 25, 24, 23, 2, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, + 12, 17, 21, 1, 26, 10, 10, 1, 7, 13, 13, 2, 23, 15, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, + 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, + 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, + 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, - 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, - 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, - 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, - 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, - 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, - 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, - 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, - 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, - 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, - 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, - 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, - 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, - 0, 0, 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, - 0,102, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104, - 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, - 0, 0, 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, - 0, 0, 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, - 0,118, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, + 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, + 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, 16, 19, + 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, 36, 0, + 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 0, 47, + 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, + 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, 56, 57, + 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, 64, 65, + 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 70, + 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, + 74, 75, 0, 0, 0, 0, 76, 77, 0, 78, 79, 0, 0, 80, 81, 0, + 82, 62, 0, 83, 84, 0, 0, 85, 86, 87, 0, 88, 0, 89, 0, 90, + 0, 0, 51, 91, 51, 0, 92, 0, 93, 0, 0, 0, 81, 0, 0, 0, + 94, 95, 0, 96, 97, 98, 99, 0, 0, 0, 0, 0, 51, 0, 0, 0, + 0,100,101, 0, 0, 0, 0, 0, 0,102, 0, 0, 0, 0, 0, 0, + 103, 0, 0, 0, 0, 0, 0,104,105, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,108,109, 0, 0,110, 0, 0, 0, 0, 0, 0,111, 0,112, 0, + 105, 0, 0, 0, 0, 0,113,114, 0, 0, 0, 0, 0, 0, 0,115, + 0, 0, 0,116, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0,118, + 0,119, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, @@ -3371,560 +3219,384 @@ _hb_ucd_u8[17524] = 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, - 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, - 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, - 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, - 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, - 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, - 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, - 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, - 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, - 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, - 0, 0, 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0, - 103, 0, 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107, - 108, 0, 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, - 33, 0,112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0, - 117, 0, 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, - 88, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, - 0,122, 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, - 0, 0, 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, - 0,128, 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0, - 134,135,136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, - 0, 0,138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, - 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, - 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, - 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, - 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, - 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, - 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, - 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, - 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, - 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, - 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, - 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, - 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, - 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, - 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, - 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, - 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, - 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, - 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, - 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, - 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, - 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, - 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, - 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, - 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, - 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, - 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, - 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, - 0, 38, 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0, - 115, 0, 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, - 0, 0, 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, - 79, 0, 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, - 0, 0, 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, - 0, 0, 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, - 48,105, 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, - 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230, - 230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220, - 220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220, - 220,230,230,230,230,240,230,220,220,220,230,230,230,220,220, 0, - 230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, - 234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230, - 230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0, - 230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, - 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, - 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, - 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230,220,230, - 220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230, - 230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, - 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230, - 230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, - 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107, - 118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, - 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, - 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, - 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, - 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220, - 230,220, 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0, - 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, - 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, - 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, - 232,222,224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230, - 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, - 0,230,220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, - 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, - 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, - 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 33, - 17, 49, 17, 17, 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, + 0, 0, 57, 58, 0, 0, 0, 59, 60, 61, 62, 0, 0, 0, 0, 63, + 52, 0, 64, 65, 0, 0, 66, 0, 0, 0, 67, 68, 0, 0, 0, 69, + 0, 70, 71, 72, 73, 74, 1, 75, 0, 76, 77, 78, 0, 0, 79, 80, + 0, 0, 0, 81, 0, 0, 1, 1, 0, 0, 82, 0, 0, 83, 0, 0, + 0, 0, 79, 84, 0, 85, 0, 0, 0, 0, 0, 80, 86, 0, 87, 0, + 52, 0, 1, 80, 0, 0, 88, 0, 0, 89, 0, 0, 0, 0, 0, 90, + 57, 0, 0, 0, 0, 0, 0, 91, 92, 0, 0, 86, 0, 0, 33, 0, + 0, 93, 0, 0, 0, 0, 94, 0, 0, 0, 0, 49, 0, 0, 95, 0, + 0, 0, 0, 96, 97, 0, 0, 98, 0, 0, 99, 0, 0, 0,100, 0, + 0, 0,101, 0, 0, 0,102, 0, 0, 0, 0,103,104, 95, 0, 0, + 105, 0, 0, 0, 86, 0, 0,106, 0, 0, 0,107,108, 0, 0,109, + 110, 0, 0, 0, 0, 0, 0,111, 0, 0,112, 0, 0, 0, 0,113, + 33, 0,114,115,116, 57, 0, 0,117, 35, 0, 0,118, 0, 0, 0, + 119, 0, 0, 0, 0, 0, 0,120, 0, 0,121, 0, 0, 0, 0,122, + 90, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,123, 0, 0, 0, + 0,124, 0, 0,125, 0, 0, 0, 0,123, 0, 0,126, 0, 0, 0, + 0, 0, 81, 0, 0, 0, 0,127, 0, 0, 0,128, 0, 0, 0,129, + 0,130, 0, 0, 0, 0,131,132,133, 0,134, 0,135, 0, 0, 0, + 136,137,138, 0, 79, 0, 0, 0, 0, 0, 35, 0, 0, 0,139, 0, + 0, 0,140, 0, 0, 0,141, 0, 0, 0,142,143, 0,144, 0, 0, + 145, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, + 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, + 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, + 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, + 0, 19, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, + 54, 21, 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, + 0, 0, 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, + 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, + 0, 0, 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, + 0, 77, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, + 0, 80, 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, + 0, 0, 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, + 1, 52, 15, 86, 36, 10, 21, 1, 1, 1, 1, 41, 1, 21, 87, 0, + 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, + 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 90, 9, 12, 4, + 91, 8, 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, 94, 1, 1, 1, + 1, 95, 96, 97, 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, + 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0,102,103, + 0, 0,104, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, 0, 0, + 0, 0, 0, 62, 0, 0,105, 68, 61, 0, 0, 0, 78, 0, 0, 0, + 106,107, 58, 38, 81, 0, 0, 0, 0, 0, 0,108, 1, 14, 4, 12, + 84, 0, 0, 0, 0, 38, 90, 0, 0, 0, 0,109, 0, 0,110, 61, + 0,111, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, + 0, 0,112, 51, 0,112, 14, 52,113, 41, 0, 0, 62, 0, 0, 61, + 0, 0,114, 0, 90, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, + 0,114, 0, 0, 0, 0,115, 0, 0, 0, 78, 55, 0, 38, 1, 58, + 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,116, 0, 0, 0, + 55, 0, 0, 0, 0,116, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, + 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, + 8, 92, 0, 0, 1, 90, 0, 0,117, 0, 0, 0, 0, 0, 0,118, + 0,119,120,121,122, 0,105, 4,123, 49, 23, 0, 0, 0, 38, 50, + 38, 58, 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, + 0, 0, 0, 1, 0, 0, 0,124, 0, 0, 0,113, 19, 59, 0, 38, + 0, 81, 0, 0, 4,123, 0, 0, 0, 1,125, 0, 0, 0, 0, 0, + 230,230,230,230,230,232,220,220,220,220,232,216,220,220,220,220, + 220,202,202,220,220,220,220,202,202,220,220,220, 1, 1, 1, 1, + 1,220,220,220,220,230,230,230,230,240,230,220,220,220,230,230, + 230,220,220, 0,230,230,230,220,220,220,220,230,232,220,220,230, + 233,234,234,233,234,234,233,230, 0, 0, 0,230, 0,220,230,230, + 230,230,220,230,230,230,222,220,230,230,220,220,230,222,228,230, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, + 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, + 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230,220,230,230,220, + 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0,220,230, + 230,220, 0, 0, 0, 36, 0, 0,230,220,230,230,220,220,230,220, + 220,230,220,230,220,230,230, 0, 0,220, 0, 0,230,230, 0,230, + 0,230,230,230,230,230, 0, 0, 0,220,220,220,230,220,220,220, + 230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, + 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, + 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0, + 107,107,107,107,118,118, 9, 0,122,122,122,122,220,220, 0, 0, + 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, + 0, 0,130,130,130,130, 0, 0,130, 0,230,230, 9, 0,230,230, + 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, + 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0, + 230, 0, 0,220,230,220, 0,220,230,230,230,234, 0, 0, 9, 9, + 0, 0, 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0, + 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, + 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, + 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, + 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, + 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, + 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, + 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, + 230,230, 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, + 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, - 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, - 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, - 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, 3, 3, 25, 3, 3, 3, - 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, 0, 1, 0, 0, 0, 0, - 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, - 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, - 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, - 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, - 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, - 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, - 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, - 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, - 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, - 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, 0, 0, 0, 0, 56, 57, - 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, 0, 0, 0, 0, 0, 59, - 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 60, - 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 3, - 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 1, 1, 0, - 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, - 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 1, 0, - 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, 1, 1, 0, 1, - 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, 22, 0, 0, 0, - 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, 8, 21, 27, 0, - 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, 0, 31, 32, 20, - 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, 0, 0, 33, 9, - 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, 35, 21, 21, 21, - 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, 1, 0, 1, 0, - 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, 8, 21, 21, 21, - 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, 21, 21, 21, 9, - 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, 0, 0, 0, 45, - 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, 9, 0, 1, 0, 1, 1, - 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, - 4, 5, 5, 5, 5, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 9, 16, 17, 18, 9, 19, 20, 21, 22, 23, 24, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 25, 26, 27, 5, 28, 29, 5, 30, 31, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 32, 0, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 20, 21, 22, 23, 24, 25, 26, 27, 28, 1, 29, 30, 31, - 32, 32, 33, 32, 32, 32, 34, 32, 32, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 46, 46, - 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 44, 57, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 95, - 95, 96, 97, 98, 56, 56, 56, 56, 56, 56, 56, 56, 56, 99,100,100, - 100,100,101,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,102,103,103,104, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,105, - 56, 56, 56, 56, 56, 56,106,106,107,108, 56,109,110,111,112,112, - 112,112,112,112,112,112,112,112,112,112,112,112,112,112,112,112, - 112,112,112,112,112,113,112,112,112,114,115,116, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,117,118,119, - 120, 56, 56, 56, 56, 56, 56, 56, 56, 56,121, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,122, 32,123,124,125,126, - 127,128,129,130,131,132,133,133,134, 56, 56, 56, 56,135,136,137, - 138, 56,139,140, 56,141,142,143, 56, 56,144,145,146, 56,147,148, - 149, 32, 32, 32,150,151,152, 32,153,154, 56, 56, 56, 56, 44, 44, - 44, 44, 44, 44,155, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44,156,157, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,158, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44,159, 44, 44,160, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 44, 44,161, 56, 56, 56, 56, 56, 44, 44, - 44,162, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44,163, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,164,165, - 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, - 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, - 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, - 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, - 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, - 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, - 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, - 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, - 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, - 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, - 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, - 3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, - 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, - 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, - 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, - 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, - 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, - 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, - 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, - 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, - 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, - 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, - 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, - 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, - 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, - 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, - 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, - 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, - 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, - 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, - 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, - 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, - 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, - 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, - 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, - 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, - 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, - 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, - 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, - 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, - 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, - 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, - 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, 2, 2, 23, 23, - 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, - 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, - 2, 2, 2, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, - 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, - 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 2, 36, - 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, 36, - 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, 2, - 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 2, 2, 2, 2, 2, 18, - 18, 2, 18, 2, 18, 18, 18, 18, 18, 2, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, - 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, - 18, 18, 18, 18, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, - 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, - 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, - 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, - 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, - 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, - 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, - 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, - 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 2, 2, 2, 2, 2, 2, 32, 2, - 2, 2, 2, 2, 2, 2, 32, 32, 32, 2, 2, 2, 2, 2, 28, 28, - 28, 28, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 2, 2, 2, 2, 48, 2, - 2, 2, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 58, 58, - 2, 2, 2, 2, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, - 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 2, 2, 54, 54, 91, 91, - 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, - 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, - 91, 91, 91, 91, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, - 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, - 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, - 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 6, - 6, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, - 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, - 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, - 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, - 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, - 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, - 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, - 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, - 19, 19, 19, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, - 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, - 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, - 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, - 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, - 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, - 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, - 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 17, 17, 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, + 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, + 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, + 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, + 3, 3, 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, + 27, 28, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, + 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, + 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, + 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, + 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, + 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, + 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, + 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, + 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, + 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, + 19, 1, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, + 0, 0, 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, + 0, 0, 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, + 0, 7, 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, + 0, 0, 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, + 15, 16, 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, + 21, 9, 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, + 26, 0, 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, + 1, 28, 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, + 1, 4, 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, + 21, 21, 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, + 39, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, + 0, 0, 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, + 1, 1, 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, + 0, 0, 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, + 21, 21, 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, + 9, 1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, + 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 13, 13, 13, + 24, 25, 26, 26, 26, 27, 13, 13, 13, 28, 29, 30, 13, 31, 32, 33, + 34, 35, 36, 37, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 38, 7, 7, 39, 7, 40, 7, 7, + 7, 41, 13, 42, 7, 7, 43, 7, 7, 7, 44, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, + 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, + 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 59, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 59, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 78, 69, 69, 69, 69, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, + 81, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 69, 69, 96, 97, 98, 99, 99, 99, + 100,101,102,103,104,105,106,107,108,109, 95,110,111,112,113,114, + 115,116,117,117,118,119,120,121,122,123,124,125,126,127,128,129, + 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, + 95,146,147,148,149, 95,150,151,152,153,154,155,156,157,158,159, + 160,161, 95,162,163,164,165,165,165,165,165,165,165,166,167,165, + 168, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95,169,170,170,170,170,170,170,170,170,171,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, + 170,170,170,170,170,170,170,170,170,170,170,170,170,172,173,173, + 173,173,174, 95, 95, 95, 95, 95,175, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95,176,176,176,176,177,178,179,180, 95, 95, + 181, 95,182,183,184,185,186,186,186,186,186,186,186,186,186,186, + 186,186,186,186,186,186,186,186,186,186,186,186,187,187,187,188, + 189,190, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95,191,192,193,194,195,195,196, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,197,198, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 59,199, + 59, 59, 59,200,201,202, 59,203,204,205,206,207,208, 95,209,210, + 211, 59, 59,212, 59,213,214,214,214,214,214,215, 95, 95, 95, 95, + 95, 95, 95, 95,216, 95,217,218,219, 95, 95,220, 95, 95, 95,221, + 95,222, 95,223, 95,224,225,226,227, 95, 95, 95, 95, 95,228,229, + 230, 95,231,232, 95, 95,233,234, 59,235,236, 95, 59, 59, 59, 59, + 59, 59, 59,237, 59,238,239,240, 59, 59,241,242, 59,243, 95, 95, + 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69,244, 69, 69,245, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69,246, 69, 69, 69, 69, 69, 69, 69, 69, 69,247, 69, 69, + 69, 69,248, 95, 95, 95, 69, 69, 69, 69,249, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69,250, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,251, 95, + 95, 95, 95, 95, 95, 95,252, 95,253,254, 0, 1, 2, 2, 0, 1, + 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, 0, + 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, + 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, + 9, 2, 9, 2, 9, 9, 9, 9, 2, 9, 9, 9, 55, 55, 55, 55, + 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 2, 2, 2, 2, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, + 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, + 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 2, 2, 64, 64, + 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, + 2, 2, 90, 90, 90, 2, 95, 95, 95, 95, 2, 2, 95, 2, 3, 3, + 2, 2, 2, 2, 2, 3, 3, 3, 0, 3, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, + 5, 5, 5, 2, 2, 5, 5, 2, 5, 5, 5, 2, 5, 2, 2, 2, + 5, 5, 5, 5, 2, 2, 5, 5, 5, 2, 2, 2, 2, 5, 5, 5, + 2, 5, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 2, 11, 2, 2, 11, 2, 11, 2, 2, 2, 11, 11, 2, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, + 2, 2, 10, 2, 2, 2, 2, 2, 10, 10, 2, 21, 21, 21, 21, 21, + 21, 21, 21, 2, 2, 21, 21, 2, 21, 21, 21, 21, 2, 2, 21, 21, + 2, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, + 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 23, 23, 23, 23, 23, 2, + 23, 23, 23, 23, 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 2, + 23, 23, 2, 2, 2, 23, 16, 16, 16, 16, 16, 2, 16, 16, 2, 16, + 16, 16, 16, 16, 2, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, + 16, 16, 20, 20, 20, 20, 20, 2, 20, 20, 2, 2, 20, 20, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, + 2, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 2, + 36, 2, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 2, 2, 2, 2, 0, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, 2, 2, 18, 2, + 18, 2, 25, 25, 25, 25, 2, 25, 25, 25, 25, 2, 2, 2, 25, 2, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 33, 33, 33, 33, 8, 8, + 8, 8, 8, 8, 2, 8, 2, 8, 2, 2, 8, 8, 8, 0, 12, 12, + 12, 12, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 2, 2, 30, 30, 30, 30, 2, 2, 2, 29, 29, 29, 29, 29, 29, + 2, 2, 28, 28, 28, 28, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, + 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 45, 45, + 45, 45, 45, 45, 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 0, + 0, 2, 43, 43, 43, 43, 46, 46, 46, 46, 46, 2, 46, 46, 31, 31, + 31, 31, 31, 31, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 2, 2, 32, 2, 2, 2, 32, 32, 32, 2, 28, 28, + 2, 2, 48, 48, 48, 48, 48, 48, 48, 2, 48, 2, 2, 2, 52, 52, + 52, 52, 52, 52, 2, 2, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, + 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 2, 2, + 54, 54, 91, 91, 91, 91, 91, 91, 91, 2, 91, 2, 2, 91, 91, 91, + 2, 2, 1, 1, 2, 2, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, + 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 6, 6, 6, 2, 8, 8, + 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, + 19, 19, 19, 19, 19, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, + 9, 9, 1, 1, 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, + 0, 19, 0, 0, 0, 2, 19, 2, 2, 2, 0, 0, 2, 2, 1, 2, + 2, 2, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, 2, 2, + 0, 0, 56, 56, 56, 56, 2, 55, 55, 55, 61, 61, 61, 61, 2, 2, + 2, 61, 61, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, + 2, 2, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, + 12, 12, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, - 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, - 2, 2, 2, 2, 2, 0, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, - 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, - 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, - 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 2, 2, 19, 19, 2, 19, 2, 19, 19, 19, 2, 2, - 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, - 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, - 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, - 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, - 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, - 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, - 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, - 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, - 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, - 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, - 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, - 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, - 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, - 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, - 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 6, 6, 0, 0, - 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, - 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, - 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, - 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, - 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, - 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, - 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, - 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, - 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, - 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, - 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, - 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, - 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,170,170, - 170,170,170,170,170,170,170,170,170,170, 2, 2, 2, 2,110,110, - 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, - 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 47, 47, - 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, - 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 2, 81,120,120,120,120,120,120,120,120,116,116, - 116,116,116,116,116,116,116,116,116,116,116,116,116, 2, 2, 2, - 2, 2, 2, 2, 2,116,128,128,128,128,128,128,128,128,128,128, - 128, 2,128,128, 2, 2, 2, 2, 2,128,128,128,128,128, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72, 98, 98, - 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, - 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, 57, - 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, - 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, - 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 2, 2, 2, 2, 88, 88, - 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,112,112, - 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, - 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, - 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2,122,122, - 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, - 122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130, - 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, - 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, - 2, 2, 2, 2, 2, 2,165,165,165,165,165,165,165,165,165,165, - 165,165,165,165, 2, 2, 2,165,165,165,165,165,165,165, 2, 2, - 2, 2, 2, 2,165,165,156,156,156,156,156,156,156,156,156,156, - 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,147,147, - 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, - 2, 2, 2, 2, 2, 2,158,158,158,158,158,158,158,158,158,158, - 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, - 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, - 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, - 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, - 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, - 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, - 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, - 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, - 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, 2, - 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, - 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, - 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, - 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, - 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, - 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, - 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, - 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, - 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, - 107,107,107, 2, 2, 2,171,171,171,171,171,171,171,171,171,171, - 2,171, 2, 2,171, 2,171,171,171,171,171,171, 2,171,171, 2, - 171, 2, 2,171, 2,171,171,171,171, 2,171,171,171,171,171, 2, - 2, 2, 2, 2, 2, 2, 2,171,171, 2, 2, 2, 2, 2,137,137, - 137,137,137,137,137,137,137,137,137,137, 2,137,137,137,137,137, - 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, - 2, 2, 2, 2, 2, 2,123,123,123,123,123,123,123,123,123,123, - 123,123,123,123, 2, 2,114,114,114,114,114,114,114,114,114,114, - 114,114,114, 2, 2, 2,114,114, 2, 2, 2, 2, 2, 2, 32, 32, - 32, 32, 32, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, - 2, 2, 2, 2, 2, 2, 33, 33, 33, 33, 2, 2, 2, 2,126,126, - 126,126,126,126,126,126,126,126,126, 2, 2,126,126,126,126,126, - 126,126, 2, 2, 2, 2,126,126,126,126,126,126,126, 2,142,142, - 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, - 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, - 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, - 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, - 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, - 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, - 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, - 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, - 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2, 7, 7, - 2, 2, 2, 2, 2, 2,169,169,169,169,169,169,169,169,169,169, - 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, - 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, - 133,133,133,133, 2, 2,133,133,133,133,133, 2, 2, 2,134,134, - 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134, 2,134, - 134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138,138, - 138,138,138,138,138, 2,138,138, 2,138,138,138,138,138,138,138, - 138,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138,138, - 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, - 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, - 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, - 2, 2, 2, 2, 2, 2,143,143, 2, 2, 2, 2, 2, 2,145,145, - 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, - 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, - 163, 2, 2, 2,163,163,163,163,163, 2, 2, 2, 2, 2, 86, 2, - 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, - 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, - 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2, 80, 80, - 80, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127, - 127,127,127,127,127, 2,166,166,166,166,166,166,166,166,166,166, - 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115, - 115,115,115,115,115,115,115,115,115,115,115,115,115, 2,115,115, - 2, 2, 2, 2,115,115,159,159,159,159,159,159,159,159,159,159, - 159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103, - 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, - 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, - 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,167,167, - 167,167,167,167,167,167,167,167, 2, 2, 2, 2, 2, 2,146,146, - 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, - 2, 2, 2, 2, 2, 99,136,139, 13, 13,155, 2, 2, 2,136,136, - 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, - 155,155,155,155, 2, 2, 2, 2, 2, 2, 2, 2, 2,155,136, 2, - 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, - 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, - 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, - 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139, - 139,139,139,139,139,139,139,139,139,139, 2, 2, 2, 2,105,105, - 105,105,105,105,105,105,105,105,105, 2, 2, 2, 2, 2,105,105, - 105,105,105, 2, 2, 2,105, 2, 2, 2, 2, 2, 2, 2,105,105, - 2, 2,105,105,105,105, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, - 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, - 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, - 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, - 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, - 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, - 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, - 131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, - 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, - 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, 6, - 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151,151, - 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, - 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,160,160, - 160,160,160,160,160,160,160,160,160,160,160,160,160, 2,152,152, - 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, - 164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2,168,168, - 168,168,168,168,168,168,168,168,168, 2, 2, 2, 2,168, 30, 30, - 30, 30, 2, 30, 30, 2,113,113,113,113,113,113,113,113,113,113, - 113,113,113, 2, 2,113,113,113,113,113,113,113,113, 2,132,132, - 132,132,132,132,132,132,132,132,132,132, 2, 2, 2, 2,132,132, - 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, - 3, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, - 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, 3, - 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, - 3, 3, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 13, 2, - 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, - 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, + 17, 0, 2, 26, 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, + 12, 2, 12, 12, 12, 0, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, + 39, 2, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, 2, 19, + 19, 19, 60, 60, 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 75, 75, + 75, 75, 75, 75, 2, 2, 2, 2, 75, 75, 69, 69, 69, 69, 69, 69, + 0, 69, 74, 74, 74, 74, 2, 2, 2, 74, 12, 2, 2, 2, 84, 84, + 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, + 33, 2, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, 2, 2, 92, 92, + 92, 92, 92, 92, 92, 2, 2, 2, 2, 92, 87, 87, 87, 87, 87, 87, + 87, 2, 19, 9, 19, 19, 19, 19, 0, 0, 87, 87, 2, 2, 2, 2, + 2, 12, 19, 19, 19, 2, 2, 2, 2, 4, 14, 2, 14, 2, 14, 14, + 2, 14, 14, 2, 14, 14, 3, 3, 0, 0, 1, 1, 6, 6, 3, 2, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 0, 0, 2, 2, 12, 12, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 2, 2, 0, 2, 2, 2, 9, 2, + 2, 2, 0, 1, 2, 2, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, + 67, 67, 67, 2, 2, 2, 42, 42, 42, 42, 2, 42, 42, 42, 41, 41, + 41, 41, 41, 41, 41, 2,118,118,118,118,118,118,118, 2, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, + 40, 40, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 2, 2,135,135, + 135,135,106,106,106,106,104,104,104,104, 2, 2, 2,104,161,161, + 161,161,161,161,161, 2,161,161, 2,161,161, 2, 2, 2,170,170, + 170,170,110,110,110,110,110,110,110, 2,110,110, 2, 2, 19, 19, + 2, 19, 19, 2, 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, + 81, 81, 81, 81, 2, 81,120,120,120,120,116,116,116,116,116,116, + 116, 2, 2, 2, 2,116,128,128,128,128,128,128,128, 2,128,128, + 2, 2, 2, 2, 2,128, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, + 72, 72, 72, 72, 2, 2, 2, 2, 2, 72,173,173,173,173,173,173, + 2, 2, 98, 98, 98, 98, 97, 97, 97, 97, 2, 2, 97, 97, 57, 57, + 57, 57, 2, 57, 57, 2, 2, 57, 57, 57, 57, 57, 2, 2, 57, 57, + 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 88, 88, 88, 88,117,117, + 117,117,112,112,112,112,112,112,112, 2, 2, 2, 2,112, 78, 78, + 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 83, 83, 83, 83, 83, 83, + 2, 2, 82, 82, 82, 82, 82, 82, 82, 2,122,122,122,122,122,122, + 2, 2, 2,122,122,122,122, 2, 2, 2, 89, 89, 89, 89, 89, 2, + 2, 2,130,130,130,130,130,130,130, 2, 2, 2,130,130,144,144, + 144,144,144,144, 2, 2,165,165,165,165,165,165, 2, 2, 2,165, + 165,165, 2, 2,165,165, 3, 3, 3, 2,156,156,156,156,156,156, + 2,156,156,156, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2,147,147, + 147,147,148,148,148,148,148,148, 2, 2,158,158,158,158,158,158, + 2, 2,153,153,153,153,149,149,149,149,149,149,149, 2, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 2, 2, 2, 94, 85, 85, + 85, 85, 85, 85, 85, 2, 2, 85, 2, 2,101,101,101,101,101, 2, + 2, 2,101,101, 2, 2, 96, 96, 96, 96, 96, 2, 96, 96,111,111, + 111,111,111,111,111, 2,100,100,100,100,108,108,108,108,108,108, + 2,108,108,108, 2, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129, 2, 2,109,109,109,109,109,109, + 109, 2,109,109, 2, 2,107,107,107,107, 2,107,107,107,107, 2, + 2,107,107, 2,107,107,107,107, 2, 1,107,107, 2, 2,107, 2, + 2, 2, 2, 2, 2,107, 2, 2,107,107,171,171,171,171,171,171, + 2,171, 2, 2,171, 2,171, 2,171, 2, 2,171, 2,171,171,171, + 171, 2,171, 2, 2, 2, 2,171,171, 2,137,137,137,137, 2,137, + 137,137,137,137, 2, 2,124,124,124,124,124,124, 2, 2,123,123, + 123,123,123,123, 2, 2,114,114,114,114,114, 2, 2, 2,114,114, + 2, 2,102,102,102,102,102,102, 2, 2,126,126,126,126,126,126, + 126, 2, 2,126,126,126,142,142,142,142,125,125,125,125,125,125, + 125, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2, 2,154,154, 2,154,154, 2,154,154, 2, 2,154,154,154, + 2, 2,150,150,150,150, 2, 2,150,150,150, 2, 2, 2,141,141, + 141,141,140,140,140,140,140,140,140, 2,121,121,121,121,121, 2, + 2, 2, 7, 7, 2, 2,169,169,169,169,169,169, 2, 2,133,133, + 133,133,133, 2,133,133,133,133,133, 2,133,133, 2, 2,133, 2, + 2, 2,134,134,134,134, 2, 2,134,134, 2,134,134,134,134,134, + 134, 2,138,138,138,138,138,138,138, 2,138,138, 2,138, 2, 2, + 138, 2,138,138, 2, 2,143,143,143,143,143,143, 2,143,143, 2, + 143,143,143,143,143, 2,143, 2, 2, 2,143,143, 2, 2,175,175, + 175,175,175,175, 2, 2,145,145,145,145,145, 2, 2, 2,163,163, + 163,163,163, 2,163,163,163,163,163, 2, 2, 2,163,163, 86, 2, + 2, 2, 63, 63, 63, 63, 63, 63, 2, 2, 63, 63, 63, 2, 63, 2, + 2, 2,157,157,157,157,157,157,157, 2, 80, 80, 80, 80, 80, 80, + 2, 2, 80, 80, 80, 2,127,127,127,127,127,127,127, 2,166,166, + 166,166,166,166, 2, 2, 79, 2, 2, 2,115,115,115,115,115,115, + 115, 2,115,115, 2, 2, 2, 2,115,115,159,159,159,159,159,159, + 159, 2,159,159, 2, 2,103,103,103,103,103,103, 2, 2,119,119, + 119,119,119,119, 2, 2,119,119, 2,119, 2,119,119,119,167,167, + 167,167,167,167, 2, 2,146,146,146,146,146,146,146, 2,172,172, + 172,172,172, 2, 2,172, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, + 2, 99,136,139, 13, 13,155, 2, 2, 2, 13, 13, 13, 2,136,136, + 136,136,155,155,155,155,155,155, 2, 2, 2, 2, 2,155,136,136, + 136, 2, 2, 17, 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 17, 17, + 17, 2, 2, 2, 15, 2, 2, 17, 2, 2,139,139,139,139,105,105, + 105,105,105,105,105, 2,105, 2, 2, 2,105,105, 2, 2, 1, 1, + 1, 2, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 2, 2, + 0, 2, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 2,131,131, + 131,131, 2, 2, 2,131, 2,131,131,131, 56, 56, 56, 2, 56, 2, + 2, 56, 56, 56, 2, 56, 56, 2, 56, 56, 6, 6, 2, 2, 2, 2, + 2, 6,151,151,151,151,151, 2, 2, 2,151,151, 2, 2, 2, 2, + 151,151,160,160,160,160,160,160,160, 2,152,152,152,152,152,152, + 2, 2, 2, 2, 2,152,164,164,164,164,164,164, 2, 2,168,168, + 168,168,168,168,168, 2, 2, 2, 2,168,174,174,174,174,174,174, + 174, 2,174,174, 2, 2, 2, 2,174,174, 2, 30, 30, 2,113,113, + 113,113,113, 2, 2,113,113,113,113, 2,132,132,132,132,132,132, + 2, 2, 2, 2,132,132, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, + 2, 3, 2, 3, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, + 2, 3, 15, 0, 0, 2, 0, 2, 2, 0, 13, 2, 2, 2, 2, 0, + 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, + 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 16, 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, - 21, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, + 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, + 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, @@ -3933,67 +3605,65 @@ _hb_ucd_u8[17524] = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, - 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, - 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 26, 27, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, + 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, - 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, - 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, - 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, - 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, - 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, + 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, + 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, - 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0, - 107, 0, 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, - 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102, + 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0, + 108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, + 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124, - 125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,128,129,130,131,132,133,134,135,136,137,138,139, - 140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, - 156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 162, 0,163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, - 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, 0, + 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0, - 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162, 0,163, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, 0, 0, 0, 0, + 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0,170, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, + 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178, - 179, 0, 0, 0,180,181,182,183,184,185,186,187,188,189,190,191, - 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, - 208,209,210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 3, 4, + 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178,179, 0, 0, 0, + 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195, + 196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211, + 212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, }; -static const uint16_t -_hb_ucd_u16[9668] = +static const uint16_t _hb_ucd_u16[10904]= { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, @@ -4020,23 +3690,23 @@ _hb_ucd_u16[9668] = 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, - 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 140, 140, 140, - 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, - 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, - 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 240, 13, 13, 13, 13, 13, 13, - 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, - 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, - 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, - 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, - 280, 209, 281, 209, 209, 209, 209, 282, 209, 283, 279, 284, 209, 285, 286, 209, - 209, 209, 176, 140, 287, 140, 271, 271, 271, 288, 209, 209, 209, 209, 289, 271, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292, - 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 294, 295, 271, 296, 209, 209, 297, 279, 298, 279, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 32, 216, 217, 140, + 218, 48, 48, 219, 220, 160, 221, 222, 223, 48, 224, 64, 48, 48, 225, 226, + 48, 48, 227, 228, 229, 64, 48, 230, 231, 9, 9, 232, 233, 234, 235, 236, + 11, 11, 237, 27, 27, 27, 238, 239, 11, 240, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, + 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 273, 274, 275, 276, 209, 277, 278, 209, 279, + 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 209, 282, 209, 209, 209, 209, 283, 209, 284, 280, 285, 209, 286, 287, 209, + 209, 209, 176, 140, 288, 140, 272, 272, 272, 289, 209, 209, 209, 209, 290, 272, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 291, 292, 209, 209, 293, + 209, 209, 209, 209, 209, 209, 294, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 295, 296, 272, 297, 209, 209, 298, 280, 299, 280, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 279, 279, 279, 279, 279, 279, 279, 279, 299, 300, 279, 279, 279, 301, 279, 302, - 209, 209, 209, 279, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209, + 280, 280, 280, 280, 280, 280, 280, 280, 300, 301, 280, 280, 280, 302, 280, 303, + 209, 209, 209, 280, 304, 209, 209, 305, 209, 209, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 306, 307, 13, 13, 13, 13, 13, 13, 308, 309, 11, 11, 310, 48, 48, 48, 311, 312, 48, 313, 314, 314, 314, 314, 32, 32, 315, 316, 317, 318, 319, 320, 140, 140, 209, 321, 209, 209, 209, 209, 209, 322, @@ -4046,7 +3716,7 @@ _hb_ucd_u16[9668] = 209, 333, 334, 209, 335, 336, 209, 209, 334, 209, 209, 336, 209, 209, 209, 209, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, 48, 337, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 229, + 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 230, 339, 48, 340, 140, 13, 13, 341, 342, 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, @@ -4062,1560 +3732,407 @@ _hb_ucd_u16[9668] = 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, - 422, 271, 271, 423, 272, 272, 272, 424, 425, 426, 427, 140, 140, 209, 209, 428, + 422, 272, 272, 423, 273, 273, 273, 424, 425, 426, 427, 140, 140, 209, 209, 428, 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 48, 48, 48, 388, 48, 48, 48, 313, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, - 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, + 48, 454, 48, 455, 48, 207, 140, 140, 48, 48, 48, 456, 272, 457, 272, 272, 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, 48, 48, 475, 192, 476, 9, 477, 11, 478, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 271, 479, 48, 48, 480, 481, 482, 140, 140, 483, - 48, 464, 484, 48, 62, 485, 140, 48, 486, 140, 140, 48, 487, 140, 48, 313, - 488, 48, 48, 489, 490, 457, 491, 492, 222, 48, 48, 493, 494, 48, 196, 192, - 495, 48, 496, 497, 498, 48, 48, 499, 222, 48, 48, 500, 501, 502, 503, 504, - 48, 97, 505, 506, 507, 140, 140, 140, 508, 509, 510, 48, 48, 511, 512, 192, - 513, 83, 84, 514, 515, 516, 517, 518, 519, 48, 48, 520, 521, 522, 523, 140, - 48, 48, 48, 524, 525, 526, 481, 140, 48, 48, 48, 527, 528, 192, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 529, 530, 531, 532, 140, 140, - 48, 48, 48, 533, 534, 192, 535, 140, 48, 48, 536, 537, 192, 538, 539, 140, - 48, 540, 541, 542, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 505, 543, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 544, - 545, 546, 48, 547, 548, 192, 140, 140, 140, 140, 549, 48, 48, 550, 551, 140, - 552, 48, 48, 553, 554, 555, 48, 48, 556, 557, 558, 48, 48, 48, 48, 196, - 559, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 560, 192, - 84, 48, 529, 561, 562, 148, 175, 563, 48, 564, 565, 566, 140, 140, 140, 140, - 567, 48, 48, 568, 569, 192, 570, 48, 571, 572, 192, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 573, - 574, 115, 48, 575, 576, 577, 140, 140, 140, 140, 140, 100, 271, 578, 579, 580, + 140, 140, 140, 140, 140, 140, 272, 479, 48, 48, 480, 481, 482, 483, 140, 484, + 48, 464, 485, 48, 62, 486, 140, 48, 487, 140, 140, 48, 488, 140, 48, 313, + 489, 48, 48, 490, 491, 457, 492, 493, 223, 48, 48, 494, 495, 48, 196, 192, + 496, 48, 497, 498, 499, 48, 48, 500, 223, 48, 48, 501, 502, 503, 504, 505, + 48, 97, 506, 507, 508, 140, 140, 140, 509, 510, 511, 48, 48, 512, 513, 192, + 514, 83, 84, 515, 516, 517, 518, 519, 520, 48, 48, 521, 522, 523, 524, 140, + 48, 48, 48, 525, 526, 527, 481, 140, 48, 48, 48, 528, 529, 192, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 530, 531, 532, 533, 140, 140, + 48, 48, 48, 534, 535, 192, 536, 140, 48, 48, 537, 538, 192, 539, 540, 140, + 48, 541, 542, 543, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 506, 544, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 545, + 546, 547, 48, 548, 549, 192, 140, 140, 140, 140, 550, 48, 48, 551, 552, 140, + 553, 48, 48, 554, 555, 556, 48, 48, 557, 558, 559, 48, 48, 48, 48, 196, + 560, 140, 140, 140, 140, 140, 561, 140, 140, 140, 140, 140, 48, 48, 562, 192, + 84, 48, 530, 563, 564, 148, 175, 565, 48, 566, 567, 568, 140, 140, 140, 140, + 569, 48, 48, 570, 571, 192, 572, 48, 573, 574, 192, 48, 48, 575, 192, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 576, + 577, 115, 48, 578, 579, 580, 140, 140, 140, 140, 140, 100, 272, 581, 582, 583, 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, - 272, 272, 272, 272, 272, 272, 581, 582, 48, 48, 48, 48, 48, 48, 48, 48, + 273, 273, 273, 273, 273, 273, 584, 585, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 583, - 48, 48, 48, 584, 585, 586, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 586, + 48, 48, 48, 587, 588, 589, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 71, 48, 48, 48, 48, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 587, 588, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 589, - 48, 48, 48, 590, 591, 592, 593, 594, 48, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 595, 48, 596, 192, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 9, 9, 11, 11, 271, 597, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 598, 599, 600, 600, 601, 602, 140, 140, 140, 140, 603, 604, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 440, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 605, - 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 606, - 48, 48, 607, 608, 140, 609, 610, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 590, 591, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 592, + 48, 48, 48, 593, 594, 595, 596, 597, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 598, 48, 599, 192, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 272, 600, 9, 601, 11, 602, 140, 140, + 48, 48, 48, 48, 603, 604, 605, 605, 606, 607, 140, 140, 140, 140, 608, 609, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 610, + 48, 200, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 48, 611, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 612, + 48, 48, 611, 613, 140, 614, 615, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, - 48, 48, 48, 48, 48, 48, 71, 151, 196, 611, 612, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 192, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 140, - 32, 32, 613, 32, 614, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 616, 617, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 618, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 619, 209, 427, 209, 620, + 32, 32, 216, 32, 621, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, - 209, 209, 615, 209, 209, 209, 616, 617, 618, 209, 619, 209, 209, 209, 287, 140, - 209, 209, 209, 209, 620, 140, 140, 140, 140, 140, 140, 140, 271, 621, 271, 621, - 209, 209, 209, 209, 209, 338, 271, 461, 140, 140, 140, 140, 140, 140, 140, 140, - 9, 622, 11, 623, 624, 625, 241, 9, 626, 627, 628, 629, 630, 9, 622, 11, - 631, 632, 11, 633, 634, 635, 636, 9, 637, 11, 9, 622, 11, 623, 624, 11, - 241, 9, 626, 636, 9, 637, 11, 9, 622, 11, 638, 9, 639, 640, 641, 642, - 11, 643, 9, 644, 645, 646, 647, 11, 648, 9, 649, 11, 650, 538, 538, 538, - 32, 32, 32, 651, 32, 32, 652, 653, 654, 655, 45, 140, 140, 140, 140, 140, - 656, 657, 658, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 659, 660, 661, 27, 27, 27, 662, 140, 663, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 151, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 666, 140, 48, 48, 667, 668, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 669, 192, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 587, 670, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 671, 200, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 672, 614, 140, 140, - 9, 9, 626, 11, 673, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 503, 271, 271, 674, 675, 140, 140, 140, 140, - 503, 271, 676, 677, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 678, 48, 679, 680, 681, 682, 683, 684, 685, 206, 686, 206, 140, 140, 140, 687, - 209, 209, 688, 209, 209, 209, 209, 209, 209, 322, 333, 689, 689, 689, 209, 323, - 690, 209, 209, 209, 209, 209, 209, 209, 209, 209, 691, 140, 140, 140, 692, 209, - 693, 209, 209, 688, 694, 695, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 696, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 697, 426, 426, - 209, 209, 209, 209, 209, 209, 209, 698, 209, 209, 209, 209, 209, 176, 688, 427, - 688, 209, 209, 209, 699, 176, 209, 209, 699, 209, 691, 688, 695, 140, 140, 140, - 209, 209, 209, 209, 209, 322, 691, 426, 700, 209, 209, 209, 701, 702, 176, 694, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 703, 209, 209, 209, 209, 209, 192, + 209, 209, 622, 209, 209, 209, 623, 624, 625, 209, 626, 209, 209, 209, 288, 140, + 209, 209, 209, 209, 627, 140, 140, 140, 140, 140, 140, 140, 272, 628, 272, 628, + 209, 209, 209, 209, 209, 338, 272, 461, 140, 140, 140, 140, 140, 140, 140, 140, + 9, 629, 11, 630, 631, 632, 242, 9, 633, 634, 635, 636, 637, 9, 629, 11, + 638, 639, 11, 640, 641, 642, 643, 9, 644, 11, 9, 629, 11, 630, 631, 11, + 242, 9, 633, 643, 9, 644, 11, 9, 629, 11, 645, 9, 646, 647, 648, 649, + 11, 650, 9, 651, 652, 653, 654, 11, 655, 9, 656, 11, 657, 539, 539, 539, + 32, 32, 32, 658, 32, 32, 659, 660, 661, 662, 45, 140, 140, 140, 140, 140, + 663, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 666, 667, 668, 27, 27, 27, 669, 140, 670, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 151, 671, 672, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 673, 140, 48, 48, 674, 675, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 676, 192, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 590, 677, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 200, 678, 679, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 680, 200, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 681, 621, 140, 140, + 9, 9, 633, 11, 682, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 504, 272, 272, 683, 684, 140, 140, 140, 140, + 504, 272, 685, 686, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 687, 48, 688, 689, 690, 691, 692, 693, 694, 206, 695, 206, 140, 140, 140, 696, + 209, 209, 697, 209, 209, 209, 209, 209, 209, 322, 333, 698, 698, 698, 209, 323, + 699, 209, 209, 209, 209, 209, 209, 209, 209, 209, 700, 140, 140, 140, 701, 209, + 702, 209, 209, 697, 703, 704, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 705, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 706, 426, 426, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 176, 697, 427, + 697, 209, 209, 209, 707, 176, 209, 209, 707, 209, 700, 697, 704, 708, 140, 140, + 209, 209, 209, 209, 209, 707, 700, 426, 709, 209, 209, 209, 710, 711, 712, 703, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 713, 209, 209, 209, 209, 209, 714, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, - 48, 48, 48, 207, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 481, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 100, 48, 48, 48, 48, 48, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, - 704, 140, 584, 584, 584, 584, 584, 584, 140, 140, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, 140, 140, + 715, 140, 587, 587, 587, 587, 587, 587, 140, 140, 140, 140, 140, 140, 140, 140, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 705, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 706, - 0, 0, 1, 1, 0, 2, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 5, 0, 6, 7, 7, 7, 8, 9, 10, 11, 12, - 13, 13, 13, 13, 14, 13, 13, 13, 13, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 23, 23, 26, 23, 27, 28, 29, 23, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 23, 23, 39, 40, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, - 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - 79, 80, 81, 82, 83, 84, 85, 82, 86, 86, 87, 88, 89, 90, 91, 82, - 92, 92, 92, 92, 92, 93, 94, 95, 96, 96, 96, 96, 96, 96, 96, 96, - 97, 97, 98, 97, 99, 100, 101, 97, 102, 97, 103, 104, 105, 106, 106, 107, - 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 109, 110, 110, 111, - 112, 113, 114, 115, 116, 116, 117, 118, 119, 120, 120, 121, 120, 122, 108, 123, - 124, 125, 126, 127, 128, 129, 130, 116, 131, 132, 133, 134, 135, 136, 137, 82, - 138, 138, 139, 138, 140, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 4, 151, 152, 153, 4, 154, 7, 7, 155, 11, 156, 157, 11, 158, 159, 160, - 161, 0, 0, 162, 163, 0, 164, 165, 0, 166, 167, 4, 168, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 170, 0, 0, 0, 0, 0, - 171, 171, 171, 171, 171, 171, 171, 171, 0, 0, 0, 172, 173, 0, 0, 0, - 174, 174, 174, 4, 175, 175, 175, 176, 93, 177, 178, 179, 180, 181, 181, 13, - 0, 0, 182, 82, 183, 184, 184, 185, 184, 184, 184, 184, 184, 184, 186, 187, - 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 96, 96, 198, 199, 0, 200, - 201, 0, 0, 202, 0, 0, 203, 204, 194, 194, 205, 0, 0, 0, 0, 0, - 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 0, 0, - 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 207, 206, 208, 209, - 210, 210, 210, 210, 210, 210, 210, 210, 210, 211, 13, 13, 13, 212, 212, 213, - 0, 214, 4, 4, 215, 4, 216, 217, 218, 219, 220, 221, 222, 222, 223, 40, - 224, 225, 226, 227, 228, 228, 229, 230, 231, 232, 233, 92, 234, 234, 235, 236, - 237, 238, 239, 240, 106, 106, 241, 242, 96, 96, 96, 96, 96, 243, 244, 245, - 82, 82, 82, 82, 82, 82, 82, 82, 184, 184, 184, 246, 184, 184, 247, 82, - 248, 249, 250, 23, 23, 23, 251, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 252, 23, 23, 253, 23, 254, 255, 256, 257, 258, 259, 23, 23, 23, 260, - 261, 1, 1, 262, 263, 201, 264, 265, 266, 267, 268, 82, 269, 269, 269, 270, - 271, 272, 11, 11, 273, 274, 187, 275, 82, 82, 82, 82, 276, 277, 278, 279, - 280, 281, 282, 283, 284, 285, 286, 82, 287, 287, 288, 289, 290, 291, 292, 293, - 294, 295, 296, 297, 298, 299, 300, 301, 302, 302, 302, 302, 302, 302, 302, 302, - 302, 303, 304, 305, 306, 307, 82, 82, 308, 309, 310, 311, 312, 313, 82, 314, - 315, 316, 82, 82, 317, 318, 319, 320, 321, 322, 323, 324, 325, 82, 326, 327, - 328, 329, 330, 331, 332, 333, 82, 82, 334, 334, 335, 82, 336, 337, 336, 338, - 339, 340, 341, 342, 343, 82, 82, 82, 82, 82, 82, 344, 345, 346, 347, 348, - 349, 350, 351, 352, 353, 354, 355, 356, 357, 357, 358, 359, 360, 360, 361, 362, - 363, 364, 365, 366, 367, 367, 367, 368, 369, 370, 371, 82, 372, 373, 374, 375, - 376, 377, 378, 379, 380, 381, 382, 383, 384, 384, 385, 386, 387, 387, 388, 82, - 82, 82, 82, 82, 389, 390, 391, 82, 392, 392, 393, 394, 395, 396, 397, 398, - 399, 400, 401, 82, 82, 82, 82, 82, 402, 403, 82, 82, 82, 404, 404, 405, - 406, 407, 408, 82, 82, 409, 410, 411, 412, 412, 413, 414, 414, 415, 416, 417, - 418, 82, 82, 82, 82, 82, 419, 420, 421, 422, 423, 424, 425, 426, 82, 82, - 427, 428, 429, 430, 431, 432, 82, 82, 82, 82, 82, 82, 82, 82, 82, 433, - 434, 435, 436, 82, 82, 437, 438, 439, 440, 440, 440, 440, 440, 440, 440, 440, - 440, 440, 440, 440, 441, 82, 82, 82, 440, 440, 440, 442, 440, 440, 440, 440, - 440, 440, 443, 82, 82, 82, 82, 82, 82, 82, 82, 82, 444, 445, 445, 446, - 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 448, 447, 447, 447, 447, 447, - 447, 447, 447, 447, 447, 447, 447, 449, 450, 450, 450, 450, 450, 450, 450, 450, - 450, 450, 451, 82, 82, 82, 82, 82, 452, 453, 82, 82, 82, 82, 82, 82, - 212, 212, 212, 212, 212, 212, 212, 212, 212, 454, 455, 456, 457, 458, 459, 460, - 461, 461, 462, 463, 464, 82, 82, 82, 82, 82, 465, 466, 82, 82, 82, 82, - 82, 82, 467, 467, 468, 82, 82, 82, 469, 469, 470, 469, 471, 82, 82, 472, - 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 474, - 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 476, 477, - 478, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 479, - 480, 191, 191, 191, 191, 191, 191, 191, 191, 481, 482, 483, 484, 484, 484, 484, - 484, 484, 484, 484, 484, 484, 484, 485, 486, 486, 486, 487, 488, 489, 82, 82, - 0, 0, 0, 0, 0, 0, 0, 490, 0, 0, 0, 0, 0, 491, 82, 82, - 7, 492, 493, 0, 0, 0, 489, 82, 0, 0, 0, 0, 0, 0, 0, 494, - 0, 495, 0, 496, 497, 498, 0, 170, 11, 11, 499, 82, 82, 82, 491, 491, - 0, 0, 500, 501, 82, 82, 82, 82, 0, 0, 502, 0, 503, 504, 505, 0, - 506, 507, 508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 509, 0, 0, - 0, 0, 0, 0, 0, 0, 510, 0, 511, 511, 511, 511, 511, 511, 511, 511, - 511, 511, 511, 511, 512, 513, 82, 82, 514, 515, 82, 82, 82, 82, 82, 82, - 516, 517, 13, 518, 519, 82, 82, 82, 520, 521, 522, 82, 82, 82, 82, 82, - 82, 82, 82, 82, 523, 524, 525, 526, 82, 82, 82, 82, 82, 82, 527, 528, - 82, 82, 82, 82, 82, 82, 529, 530, 82, 82, 82, 82, 82, 82, 82, 531, - 532, 532, 532, 532, 532, 532, 533, 82, 534, 534, 535, 82, 82, 82, 82, 82, - 82, 82, 82, 536, 0, 537, 82, 82, 261, 182, 82, 82, 82, 82, 82, 82, - 538, 539, 540, 541, 542, 543, 82, 544, 0, 545, 0, 0, 491, 546, 547, 494, - 0, 0, 0, 0, 0, 548, 82, 549, 550, 551, 552, 553, 82, 82, 82, 82, - 0, 0, 0, 0, 0, 0, 554, 555, 0, 0, 0, 556, 0, 0, 490, 557, - 545, 0, 558, 0, 559, 560, 561, 82, 0, 0, 491, 562, 563, 0, 564, 565, - 0, 0, 0, 0, 258, 0, 0, 490, 184, 184, 184, 184, 184, 184, 184, 82, - 184, 247, 184, 184, 184, 184, 184, 184, 566, 184, 184, 184, 184, 184, 184, 184, - 184, 184, 184, 184, 184, 567, 184, 184, 184, 184, 184, 184, 184, 184, 184, 568, - 184, 184, 566, 82, 82, 82, 82, 82, 566, 82, 82, 82, 82, 82, 82, 82, - 184, 184, 569, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 570, 82, 82, - 571, 0, 0, 0, 82, 82, 82, 82, 7, 7, 7, 7, 7, 7, 7, 572, - 0, 0, 0, 0, 1, 2, 2, 3, 0, 4, 0, 4, 2, 2, 5, 2, - 2, 2, 2, 2, 2, 2, 2, 6, 7, 8, 0, 0, 9, 9, 9, 9, - 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, - 16, 17, 14, 14, 18, 18, 18, 18, 19, 18, 18, 18, 18, 18, 20, 21, - 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, - 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, - 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 29, 37, 38, 37, 37, - 37, 37, 37, 37, 37, 39, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, - 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, - 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 52, 31, 31, 31, - 53, 53, 53, 53, 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, - 59, 60, 61, 62, 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, - 71, 72, 73, 74, 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, - 83, 84, 85, 86, 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, - 95, 96, 97, 98, 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, - 107, 104, 108, 109, 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, - 113, 115, 113, 116, 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, - 122, 124, 125, 126, 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, - 131, 131, 131, 131, 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, - 137, 137, 140, 141, 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, - 147, 147, 147, 148, 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, - 153, 152, 152, 154, 155, 156, 152, 157, 158, 158, 158, 158, 158, 159, 158, 158, - 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, 158, 161, 162, 163, - 164, 164, 164, 164, 165, 165, 165, 165, 166, 167, 165, 165, 165, 165, 165, 168, - 169, 169, 169, 169, 170, 170, 170, 170, 170, 171, 172, 171, 170, 171, 170, 170, - 170, 170, 171, 172, 171, 170, 172, 170, 170, 170, 171, 170, 170, 170, 170, 173, - 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, 176, 176, 177, 177, - 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, 181, 182, 181, 183, - 184, 184, 185, 186, 187, 187, 188, 26, 189, 189, 190, 26, 191, 192, 193, 26, - 194, 194, 194, 194, 194, 194, 194, 195, 194, 196, 194, 196, 197, 198, 198, 199, - 198, 198, 198, 198, 198, 198, 198, 200, 198, 201, 178, 178, 178, 178, 202, 26, - 203, 203, 203, 204, 203, 205, 203, 205, 206, 203, 207, 207, 207, 208, 209, 26, - 210, 210, 210, 210, 210, 211, 210, 210, 210, 212, 210, 213, 214, 214, 214, 215, - 216, 216, 216, 216, 216, 216, 216, 217, 216, 216, 216, 218, 216, 219, 216, 219, - 216, 220, 9, 9, 9, 221, 26, 26, 222, 222, 222, 222, 222, 223, 222, 222, - 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, 226, 227, 228, 228, 228, 228, - 228, 228, 228, 229, 228, 230, 231, 231, 231, 231, 231, 231, 18, 232, 165, 165, - 165, 165, 165, 233, 224, 26, 234, 9, 235, 236, 237, 238, 239, 240, 2, 2, - 2, 2, 2, 241, 242, 243, 2, 244, 2, 2, 2, 245, 14, 14, 246, 246, - 246, 246, 14, 247, 14, 14, 14, 246, 14, 14, 248, 14, 248, 14, 249, 250, - 14, 14, 251, 252, 0, 253, 0, 0, 254, 0, 255, 256, 0, 257, 2, 258, - 259, 26, 9, 9, 9, 9, 260, 26, 261, 262, 4, 0, 0, 263, 0, 0, - 2, 264, 0, 0, 0, 265, 26, 26, 0, 266, 26, 26, 267, 267, 267, 267, - 0, 0, 268, 0, 0, 0, 269, 0, 270, 270, 270, 270, 17, 17, 17, 17, - 17, 17, 271, 272, 166, 167, 273, 273, 273, 273, 273, 273, 273, 274, 275, 274, - 170, 170, 172, 26, 172, 172, 172, 172, 0, 0, 0, 276, 277, 277, 277, 278, - 277, 277, 277, 277, 277, 277, 279, 26, 277, 277, 280, 26, 26, 26, 0, 0, - 281, 0, 0, 0, 282, 283, 0, 284, 285, 286, 286, 286, 286, 286, 286, 286, - 286, 286, 287, 288, 289, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, - 292, 293, 293, 293, 293, 293, 294, 169, 169, 295, 0, 0, 293, 293, 293, 293, - 276, 296, 290, 290, 169, 169, 169, 295, 169, 169, 169, 297, 0, 0, 290, 290, - 290, 290, 290, 298, 290, 290, 290, 0, 299, 299, 299, 299, 299, 300, 299, 299, - 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 304, 26, 26, - 305, 305, 305, 305, 305, 305, 305, 26, 306, 2, 2, 2, 2, 307, 2, 2, - 2, 308, 309, 258, 26, 26, 310, 2, 311, 311, 311, 311, 311, 312, 0, 265, - 313, 313, 313, 313, 313, 313, 313, 26, 314, 314, 314, 314, 315, 316, 314, 317, - 318, 318, 318, 318, 318, 319, 320, 320, 320, 320, 321, 322, 169, 169, 169, 323, - 324, 324, 324, 324, 324, 325, 324, 326, 164, 164, 164, 327, 328, 328, 328, 328, - 328, 328, 329, 26, 328, 330, 328, 331, 332, 332, 332, 332, 333, 26, 26, 334, - 335, 335, 336, 26, 337, 337, 337, 26, 172, 172, 2, 2, 2, 2, 2, 338, - 339, 340, 176, 176, 335, 335, 335, 335, 335, 341, 335, 342, 343, 26, 169, 169, - 295, 344, 169, 169, 169, 169, 169, 343, 277, 280, 277, 277, 277, 277, 277, 345, - 346, 26, 347, 348, 25, 25, 349, 350, 351, 25, 31, 31, 352, 26, 353, 31, - 31, 31, 31, 354, 31, 31, 355, 31, 31, 356, 26, 26, 26, 26, 31, 31, - 9, 9, 0, 265, 9, 357, 0, 0, 0, 0, 358, 0, 257, 359, 360, 31, - 31, 31, 31, 361, 362, 0, 0, 0, 363, 290, 289, 290, 290, 290, 290, 364, - 365, 365, 365, 366, 257, 257, 26, 367, 368, 369, 368, 368, 370, 368, 368, 371, - 368, 372, 368, 372, 368, 368, 368, 368, 368, 368, 368, 373, 374, 0, 0, 0, - 0, 0, 375, 0, 14, 252, 0, 376, 377, 26, 26, 26, 0, 0, 0, 378, - 379, 379, 379, 380, 381, 381, 381, 381, 381, 381, 382, 26, 383, 0, 0, 359, - 384, 384, 384, 384, 385, 386, 387, 387, 387, 388, 389, 389, 389, 389, 389, 390, - 391, 391, 391, 392, 393, 393, 393, 393, 394, 393, 395, 26, 396, 396, 396, 396, - 396, 396, 397, 397, 397, 397, 397, 397, 398, 398, 398, 399, 398, 400, 401, 401, - 401, 401, 402, 401, 401, 401, 401, 402, 403, 403, 403, 403, 403, 26, 404, 404, - 404, 404, 404, 404, 405, 406, 407, 408, 407, 408, 409, 407, 410, 407, 410, 411, - 412, 412, 412, 412, 412, 412, 413, 26, 414, 414, 414, 414, 414, 414, 415, 26, - 414, 414, 416, 26, 414, 26, 26, 26, 417, 2, 2, 2, 2, 2, 418, 419, - 420, 421, 422, 422, 422, 422, 423, 424, 425, 425, 426, 425, 427, 427, 427, 427, - 428, 428, 428, 429, 430, 428, 26, 26, 431, 431, 432, 433, 434, 434, 434, 435, - 436, 436, 436, 437, 438, 438, 438, 438, 439, 439, 439, 440, 439, 439, 441, 439, - 439, 439, 439, 439, 442, 443, 444, 445, 446, 446, 447, 448, 446, 449, 446, 449, - 450, 450, 450, 450, 451, 451, 451, 451, 452, 452, 452, 452, 453, 454, 453, 26, - 455, 455, 455, 455, 455, 455, 456, 457, 458, 458, 459, 458, 460, 460, 461, 460, - 462, 462, 463, 464, 26, 465, 26, 26, 466, 466, 466, 466, 466, 467, 26, 26, - 468, 468, 468, 468, 468, 468, 469, 26, 468, 468, 469, 470, 471, 471, 471, 471, - 471, 26, 471, 472, 473, 473, 473, 473, 474, 475, 473, 473, 474, 476, 26, 26, - 31, 31, 31, 50, 477, 477, 477, 477, 477, 478, 479, 26, 480, 26, 26, 26, - 26, 26, 26, 481, 482, 482, 482, 482, 482, 26, 483, 483, 483, 483, 483, 484, - 26, 26, 485, 485, 485, 486, 26, 26, 26, 26, 487, 487, 487, 488, 26, 26, - 489, 489, 490, 26, 491, 491, 491, 491, 491, 492, 493, 491, 491, 491, 492, 494, - 495, 495, 495, 495, 496, 497, 498, 498, 498, 499, 498, 500, 501, 501, 501, 501, - 501, 501, 502, 501, 501, 26, 503, 503, 503, 503, 504, 26, 505, 505, 505, 505, - 506, 137, 507, 26, 508, 508, 509, 508, 508, 508, 508, 508, 510, 26, 26, 26, - 511, 512, 513, 514, 513, 515, 516, 516, 516, 516, 516, 516, 516, 517, 516, 518, - 519, 520, 521, 522, 522, 523, 524, 525, 520, 526, 527, 528, 529, 530, 530, 26, - 531, 532, 531, 531, 531, 531, 533, 531, 534, 535, 533, 536, 537, 26, 26, 26, - 538, 538, 538, 538, 538, 538, 538, 539, 540, 26, 26, 26, 541, 541, 541, 541, - 541, 26, 541, 542, 543, 543, 543, 543, 543, 543, 544, 543, 543, 543, 543, 544, - 545, 545, 545, 545, 546, 26, 545, 547, 198, 548, 26, 26, 549, 549, 549, 549, - 549, 549, 549, 550, 549, 550, 164, 164, 551, 26, 26, 26, 552, 552, 552, 553, - 552, 554, 552, 552, 555, 26, 26, 26, 556, 556, 556, 556, 556, 556, 556, 557, - 558, 558, 558, 558, 558, 558, 559, 560, 561, 562, 563, 564, 564, 564, 565, 566, - 561, 26, 564, 567, 568, 569, 568, 568, 568, 568, 568, 569, 570, 26, 26, 26, - 571, 571, 571, 571, 571, 26, 572, 572, 572, 572, 572, 572, 573, 26, 178, 178, - 574, 574, 574, 574, 574, 574, 574, 575, 53, 576, 26, 26, 577, 577, 577, 577, - 578, 26, 577, 578, 579, 580, 579, 579, 579, 579, 581, 579, 582, 26, 579, 579, - 579, 583, 584, 584, 584, 584, 585, 584, 584, 586, 587, 26, 588, 589, 590, 590, - 590, 590, 588, 591, 590, 26, 590, 592, 593, 594, 595, 595, 595, 596, 597, 598, - 595, 599, 26, 26, 600, 600, 600, 601, 602, 602, 603, 602, 602, 602, 602, 604, - 602, 602, 602, 605, 26, 26, 606, 26, 108, 108, 108, 108, 108, 108, 607, 608, - 609, 609, 609, 609, 609, 609, 609, 610, 609, 611, 612, 26, 613, 26, 26, 26, - 26, 26, 614, 614, 614, 614, 614, 614, 614, 614, 615, 26, 616, 616, 616, 616, - 616, 616, 617, 26, 616, 616, 616, 618, 619, 619, 619, 619, 620, 26, 26, 26, - 621, 621, 621, 621, 621, 621, 621, 622, 305, 305, 305, 623, 624, 624, 624, 625, - 624, 626, 627, 627, 627, 627, 627, 627, 627, 627, 627, 628, 627, 629, 630, 630, - 630, 631, 631, 26, 632, 632, 632, 632, 633, 26, 632, 634, 634, 632, 632, 635, - 632, 632, 26, 26, 636, 636, 636, 636, 636, 636, 636, 637, 638, 638, 638, 638, - 638, 638, 638, 639, 640, 640, 640, 640, 640, 641, 640, 640, 640, 642, 640, 640, - 643, 26, 345, 26, 644, 644, 644, 644, 644, 644, 644, 26, 645, 645, 645, 645, - 645, 645, 646, 26, 26, 26, 26, 647, 644, 648, 26, 26, 26, 26, 649, 650, - 651, 286, 286, 286, 652, 26, 653, 26, 26, 26, 654, 26, 655, 26, 656, 656, - 656, 656, 656, 656, 656, 656, 656, 657, 658, 658, 658, 658, 658, 659, 658, 660, - 658, 661, 658, 662, 359, 26, 26, 26, 0, 0, 0, 265, 0, 0, 359, 26, - 9, 663, 9, 9, 221, 26, 0, 0, 0, 0, 276, 26, 257, 362, 0, 0, - 664, 665, 0, 666, 667, 668, 0, 0, 0, 669, 0, 0, 246, 26, 26, 26, - 0, 0, 257, 26, 0, 0, 0, 259, 0, 0, 254, 0, 0, 0, 0, 254, - 670, 671, 0, 672, 673, 0, 0, 0, 269, 674, 254, 254, 0, 0, 0, 675, - 676, 677, 678, 0, 276, 0, 0, 0, 0, 268, 0, 0, 679, 679, 679, 679, - 679, 680, 26, 681, 682, 679, 26, 26, 2, 2, 2, 346, 683, 419, 26, 26, - 684, 270, 270, 685, 686, 687, 18, 18, 18, 688, 26, 26, 26, 689, 26, 26, - 690, 690, 690, 690, 690, 691, 690, 692, 690, 693, 26, 26, 26, 26, 694, 694, - 694, 695, 26, 26, 696, 696, 696, 696, 696, 696, 696, 697, 26, 26, 698, 698, - 698, 698, 698, 699, 26, 26, 700, 700, 700, 700, 700, 701, 172, 702, 170, 172, - 703, 703, 703, 703, 704, 703, 705, 26, 706, 706, 706, 706, 706, 707, 706, 708, - 26, 26, 362, 0, 0, 0, 376, 26, 709, 31, 31, 31, 710, 711, 712, 713, - 714, 715, 710, 716, 710, 712, 712, 717, 31, 718, 31, 719, 720, 718, 31, 719, - 26, 26, 721, 26, 0, 359, 0, 0, 0, 257, 362, 0, 362, 0, 362, 0, - 0, 276, 26, 26, 722, 0, 0, 0, 723, 26, 0, 0, 0, 0, 0, 359, - 0, 259, 265, 26, 276, 26, 26, 26, 0, 0, 0, 724, 0, 376, 0, 376, - 0, 0, 257, 725, 0, 359, 259, 26, 0, 26, 0, 265, 0, 26, 0, 0, - 0, 276, 0, 359, 265, 26, 26, 26, 0, 276, 0, 376, 0, 726, 0, 0, - 257, 722, 0, 727, 0, 265, 0, 259, 277, 277, 277, 280, 345, 26, 277, 277, - 728, 26, 277, 277, 277, 729, 277, 277, 277, 277, 26, 26, 730, 26, 26, 26, - 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976, - 1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082, - 1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161, - 1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269, - 1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, - 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, - 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191, - 1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025, - 1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0, - 1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252, - 1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271, - 1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120, - 1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1093,1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339, - 1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241, - 1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348, - 1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197, - 1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, - 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364, - 1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0, - 1450, 0,1455,1461,1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458, - 1468,1476,1480,1486,1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503, - 1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0, - 1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, - 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0, - 1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571, - 1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573, - 1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, - 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, - 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617, - 1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628, - 1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, - 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638, - 1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644, - 1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, - 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653, - 1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0, - 1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0, - 1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, - 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0, - 1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0, - 1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, - 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, - 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170, - 1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185, - 1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213, - 1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224, - 1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249, - 1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259, - 1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393, - 1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295, - 1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, - 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345, - 1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, - 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198, - 1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401, - 1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410, - 1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, - 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719, - 1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735, - 1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755, - 1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774, - 1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783, - 1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, - 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812, - 1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28, - 1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724, - 1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760, - 1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817, - 1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12, - 1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14, - 1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, - 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17, - 1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18, - 1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0, - 1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, - 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, - 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872, - 1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0, - 1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, - 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0, - 1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0, - 1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, - 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0, - 1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, - 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, - 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, - 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, - 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, - 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, - 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, - 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, - 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, - 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, - 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, - 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, - 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, - 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, - 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, - 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, - 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, - 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, - 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, - 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, - 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, - 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, - 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, - 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, - 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, - 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, - 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, - 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, - 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, - 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, - 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603, - 1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589, - 1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582, - 1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1937, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, - 1939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1940, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1943, - 1944, 0, 0, 0, 0, 0, 0,1945, 0,1946, 0, 0, 0, 0, 0, 0, - 0, 0,1947, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1950, 0,1949,1951, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1953, - 1952, 0,1954, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1955,1956, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1957, 0, 0, 0, - 0, 0, 0, 0, 0,1958,1961,1959,1965,1960,1962,1964,1963, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1967,1966,1968, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1969,1970,1971,1972,1973,1974,1975, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1976, - 1977,1978,1980,1979,1981, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, - 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, - 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, - 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, - 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, - 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, - 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, - 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, - 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, - 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, - 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, - 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, - 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, - 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, - 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, - 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, - 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, - 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, - 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, - 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, - 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, - 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, - 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, - 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, - 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, - 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, - 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, - 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, - 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, - 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, - 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, - 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, - 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, - 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, - 821, 935, 0, 0, -}; -static const int16_t -_hb_ucd_i16[92] = -{ - 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, - 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, - 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, - 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, - -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, - 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, -}; - -static inline uint_fast8_t -_hb_ucd_gc (unsigned u) -{ - return u<1114110u?_hb_ucd_u8[6472+(((_hb_ucd_u8[816+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>4>>4])<<4)+((u>>1>>3>>4)&15u))])<<4)+((u>>1>>3)&15u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; -} -static inline uint_fast8_t -_hb_ucd_ccc (unsigned u) -{ - return u<125259u?_hb_ucd_u8[8504+(((_hb_ucd_u8[7936+(((_hb_ucd_u8[7460+(((_hb_ucd_u8[7100+(((_hb_ucd_u8[6854+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; -} -static inline unsigned -_hb_ucd_b4 (const uint8_t* a, unsigned i) -{ - return (a[i>>1]>>((i&1u)<<2))&15u; -} -static inline int_fast16_t -_hb_ucd_bmg (unsigned u) -{ - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9396+(((_hb_ucd_u8[9164+(((_hb_ucd_u8[9068+(((_hb_ucd_b4(9004+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; -} -static inline uint_fast8_t -_hb_ucd_sc (unsigned u) -{ - return u<918000u?_hb_ucd_u8[10398+(((_hb_ucd_u16[3952+(((_hb_ucd_u16[2624+(((_hb_ucd_u8[9870+(((_hb_ucd_u8[9644+(u>>3>>2>>3>>4)])<<4)+((u>>3>>2>>3)&15u))])<<3)+((u>>3>>2)&7u))])<<2)+((u>>3)&3u))])<<3)+((u)&7u))]:2; -} -static inline uint_fast16_t -_hb_ucd_dm (unsigned u) -{ - return u<195102u?_hb_ucd_u16[6244+(((_hb_ucd_u8[16628+(((_hb_ucd_u8[16246+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; -} - - -#else - -static const uint8_t -_hb_ucd_u8[13730] = -{ - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 11, 12, 12, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 7, 21, 22, 22, 22, 23, 24, 7, 7, - 7, 25, 22, 22, 22, 26, 27, 28, 22, 29, 30, 31, 32, 33, 34, 35, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 22, 36, - 7, 7, 7, 7, 37, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 38, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 34, 34, 34, 35, 36, 37, 34, 34, 34, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, - 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, - 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, - 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, - 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, - 34, 34,109,110,111,112,113,114,115,116,117,118, 34, 34, 34,119, - 120,121,122,123,124,125,126,127, 34,128,129,130,131,132,133,134, - 135,136,137,138,139,140,141,142,143,144,111,145,146,147,148,111, - 149,150,151,152,153,154,155,156,157,158,159,160,111,161,162,163, - 34, 34, 34, 34, 34, 34, 34, 34,164, 34, 34,111,111,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,165, - 34, 34, 34, 34, 34, 34, 34, 34,166, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, - 111,111,167,111,111,111,111,111,111,111,111,111,111,111,111,111, - 34, 34, 34, 34,168,169,170, 34,111,111,171,111,172,173,174,175, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,119, - 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111, 34,176,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111, 67,177, 67, 67, 67, 67,178, 67, - 67, 67,179,180,181,131, 65,111,182,183,184,185,186,187,188,189, - 67, 67, 67, 67,190,191,111,111,111,111,111,111,111,111,192,111, - 193,194,195,111,111,196,111,111,111,197,111,198,111,111,111, 34, - 34,199,200,111,111,111,111,111,131,201,202,111, 34,203,111,111, - 67, 67,204, 67, 67,111, 67,205, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67,177,111,111,111,111,111,111,111,111, - 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, - 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111, - 206,111,194,194,111,111,111,111,111,111,111,111,111,111,111,111, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, - 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, - 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, - 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, - 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, - 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, - 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, - 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, - 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, - 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 16, 44, 16, 10, - 41, 41, 41, 45, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 46, 34, 32, 34, 11, - 32, 47, 43, 43, 48, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 11, 11, 11, 11, 49, 2, 2, 2, 16, 16, 16, 16, 50, 51, 52, 53, - 54, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 55, - 56, 57, 43, 56, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 58, 2, 2, 2, 2, 2, 2, 59, 59, 59, 8, 9, 60, 2, 61, - 43, 43, 43, 43, 43, 57, 62, 2, 63, 36, 36, 36, 36, 64, 43, 43, - 7, 7, 7, 7, 7, 2, 2, 36, 65, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 66, 43, 43, 43, 67, 47, 43, 43, 68, 69, 70, 43, 43, 36, - 7, 7, 7, 7, 7, 36, 71, 72, 2, 2, 2, 2, 2, 2, 2, 73, - 64, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 65, 36, - 36, 36, 36, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7, 36, 36, 36, - 36, 36, 36, 36, 36, 64, 43, 43, 43, 43, 40, 21, 2, 40, 69, 20, - 36, 36, 36, 43, 43, 69, 43, 43, 43, 43, 69, 43, 69, 43, 43, 43, - 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 64, 43, 43, 2, - 36, 36, 36, 36, 74, 36, 36, 36, 59, 59, 59, 75, 43, 43, 43, 43, - 36, 36, 36, 36, 76, 43, 43, 43, 43, 75, 43, 43, 43, 43, 43, 43, - 43, 77, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 65, 78, - 79, 43, 43, 43, 77, 78, 79, 78, 64, 43, 43, 43, 36, 36, 36, 36, - 36, 43, 2, 7, 7, 7, 7, 7, 80, 36, 36, 36, 36, 36, 36, 36, - 64, 78, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 78, - 79, 43, 43, 77, 78, 78, 79, 36, 36, 36, 36, 82, 78, 78, 36, 36, - 36, 43, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 53, 58, 43, - 43, 77, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 78, - 79, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 65, 36, 36, 36, - 36, 36, 36, 7, 7, 7, 7, 7, 43, 36, 64, 2, 2, 2, 2, 2, - 79, 43, 43, 43, 77, 78, 79, 43, 60, 20, 20, 20, 83, 43, 43, 43, - 43, 78, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 79, - 79, 43, 43, 77, 78, 78, 79, 43, 43, 43, 43, 77, 78, 78, 36, 36, - 72, 27, 27, 27, 27, 27, 27, 27, 43, 65, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 78, 77, 78, 78, 78, 78, 78, 79, 43, - 36, 36, 36, 82, 78, 78, 78, 78, 78, 78, 78, 7, 7, 7, 7, 7, - 27, 84, 61, 61, 53, 61, 61, 61, 77, 78, 65, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 65, 43, 77, 78, 78, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 36, 36, 36, 36, 7, 7, 7, 85, 27, 27, 27, 84, - 64, 78, 66, 36, 36, 36, 36, 36, 78, 78, 78, 77, 78, 78, 43, 43, - 43, 43, 77, 78, 78, 78, 81, 36, 86, 82, 78, 78, 78, 78, 78, 78, - 43, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 78, - 79, 43, 43, 78, 78, 78, 79, 71, 61, 61, 36, 82, 27, 27, 27, 87, - 27, 27, 27, 27, 84, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 77, - 78, 43, 43, 43, 78, 78, 78, 78, 7, 78, 2, 2, 2, 2, 2, 2, - 64, 36, 43, 43, 43, 43, 43, 88, 36, 36, 36, 69, 43, 43, 43, 57, - 7, 7, 7, 7, 7, 2, 2, 2, 64, 36, 43, 43, 43, 43, 65, 36, - 36, 36, 36, 40, 43, 43, 43, 43, 7, 7, 7, 7, 7, 7, 36, 36, - 71, 61, 2, 2, 2, 2, 2, 2, 2, 89, 89, 61, 43, 61, 61, 61, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 47, 47, 47, 4, 4, 78, - 64, 43, 43, 43, 43, 43, 43, 77, 43, 43, 57, 43, 36, 36, 64, 43, - 43, 43, 43, 43, 43, 43, 43, 61, 61, 61, 61, 70, 61, 61, 61, 61, - 2, 2, 89, 61, 21, 2, 2, 2, 36, 36, 36, 36, 36, 82, 79, 43, - 77, 43, 43, 43, 79, 77, 79, 65, 36, 36, 36, 78, 43, 36, 36, 43, - 65, 78, 81, 82, 78, 78, 78, 36, 64, 43, 65, 36, 36, 36, 36, 36, - 36, 77, 79, 77, 78, 78, 79, 82, 7, 7, 7, 7, 7, 78, 79, 61, - 16, 16, 16, 16, 16, 50, 44, 16, 36, 36, 36, 36, 36, 36, 64, 43, - 2, 2, 2, 2, 90, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 61, 61, 61, 61, 61, 61, 61, 61, 11, 11, 11, 11, 16, 16, 16, 16, - 91, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 71, 66, - 92, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 93, 94, 94, - 36, 36, 36, 36, 36, 58, 2, 95, 96, 36, 36, 36, 36, 36, 36, 36, - 36, 43, 77, 78, 78, 78, 78, 81, 36, 43, 97, 2, 2, 2, 2, 2, - 36, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 79, 43, 43, 43, 78, - 78, 78, 78, 77, 79, 43, 43, 43, 43, 43, 2, 80, 2, 60, 64, 43, - 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 98, 2, 56, 43, 75, - 36, 76, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 36, 36, 36, 36, - 36, 36, 36, 36, 64, 36, 36, 36, 43, 77, 78, 79, 77, 78, 78, 78, - 78, 77, 78, 78, 79, 43, 43, 43, 61, 61, 2, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 27, 27, 61, 36, 36, 36, 64, 77, 79, 43, 2, - 36, 36, 82, 77, 43, 43, 43, 43, 77, 77, 79, 43, 43, 43, 77, 78, - 78, 79, 43, 43, 43, 43, 43, 43, 2, 2, 2, 80, 2, 2, 2, 2, - 43, 43, 43, 43, 43, 43, 43, 99, 43, 43, 81, 36, 36, 36, 36, 36, - 36, 36, 77, 43, 43, 77, 77, 78, 78, 77, 81, 36, 36, 36, 36, 2, - 89, 61, 61, 61, 61, 47, 43, 43, 43, 43, 61, 61, 61, 61, 21, 2, - 43, 81, 36, 36, 36, 36, 36, 36, 82, 43, 43, 78, 43, 79, 43, 36, - 36, 36, 36, 77, 43, 78, 79, 79, 43, 78, 78, 78, 78, 78, 2, 2, - 36, 36, 78, 78, 78, 78, 43, 43, 43, 43, 78, 43, 43, 57, 2, 2, - 7, 7, 7, 7, 7, 7, 86, 36, 36, 36, 36, 36, 40, 40, 40, 2, - 16, 16, 16, 16, 34, 16, 16, 16, 43, 57, 43, 43, 43, 43, 43, 43, - 77, 43, 43, 43, 65, 36, 64, 36, 36, 36, 65, 82, 43, 36, 36, 36, - 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 44, 16, 16, - 16, 16, 16, 16, 44, 16, 16, 16, 16, 16, 16, 16, 16,100, 40, 40, - 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, - 16, 16, 16, 16, 34, 11, 11, 11, 16, 16, 16, 16,101,101,101,101, - 16, 16, 16, 16, 11, 11,102,103, 41, 16, 16, 16, 11, 11,102, 41, - 16, 16, 16, 16, 11, 11,104, 41,105,105,105,105,105,106, 59, 59, - 51, 51, 51, 2,107,108,107,108, 2, 2, 2, 2,109, 59, 59,110, - 2, 2, 2, 2,111,112, 2,113,114, 2,115,116, 2, 2, 2, 2, - 2, 9,114, 2, 2, 2, 2,117, 59, 59, 59, 59, 59, 59, 59, 59, - 118, 40, 27, 27, 27, 8,115,119, 27, 27, 27, 27, 27, 8,115, 94, - 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,120, 48, - 99, 48, 99, 43, 43, 43, 43, 43, 61,121, 61,122, 61, 34, 11, 16, - 11, 32,122, 61, 46, 11, 11, 61, 61, 61,121,121,121, 11, 11,123, - 11, 11, 35, 36, 39, 61, 16, 11, 8, 8, 46, 16, 16, 26, 61,124, - 95, 95, 95, 95, 95, 95, 95, 95, 95,125,126, 95,127, 61, 61, 61, - 8, 8,128, 61, 61, 8, 61, 61,128, 26, 61,128, 61, 61, 61,128, - 61, 61, 61, 61, 61, 61, 61, 8, 61,128,128, 61, 61, 61, 61, 61, - 61, 61, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 61, 61, 61, 61, 4, 4, 61, 61, 8, 61, 61, 61,129,130, 61, 61, - 61, 61, 61, 61, 61, 61,128, 61, 61, 61, 61, 61, 61, 26, 8, 8, - 8, 8, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, - 8, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 27, 61, 61, - 61, 61, 61, 61, 61, 27, 27, 27, 61, 61, 61, 26, 61, 61, 61, 61, - 26, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, 8, 8, - 61, 61, 61, 61, 61, 61, 61, 26, 61, 61, 61, 61, 4, 4, 4, 4, - 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, - 8, 8,115,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, - 8,115,132,132,132,132,132,132,132,132,132,132,131, 8, 8, 8, - 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, - 8, 8,128, 26, 8, 8,128, 61, 32, 11, 32, 34, 34, 34, 34, 11, - 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,124, 61, 61,122, 34,133, - 43, 32, 16, 16, 50, 2, 90, 2, 36, 36, 36, 36, 36, 36, 36, 76, - 2, 2, 2, 2, 2, 2, 2, 56, 2,107,107, 2,111,112,107, 2, - 2, 2, 2, 6, 2, 98,107, 2,107, 4, 4, 4, 4, 2, 2, 80, - 2, 2, 2, 2, 2, 51, 2, 2, 98,134, 2, 2, 2, 2, 2, 2, - 61, 2,135,132,132,132,136, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 1, 2,137,138, 4, 4, 4, 4, 4, 61, 4, 4, 4, 4,139, 94, - 140, 95, 95, 95, 95, 43, 43, 78,141, 40, 40, 61, 95,142, 58, 61, - 72, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64,143,144, 63, - 36, 36, 36, 36, 36, 58, 40, 63, 61, 27, 27, 61, 61, 61, 61, 61, - 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, 27, - 145, 27, 27, 27, 27, 27, 27, 27, 36, 36, 76, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36,146, 2, 32, 32, 32, 32, 32, 32, 32, 64, - 48,147, 43, 43, 43, 43, 43, 80, 32, 32, 32, 32, 32, 32, 40, 43, - 36, 36, 36, 95, 95, 95, 95, 95, 43, 2, 2, 2, 2, 2, 2, 2, - 41, 41, 41,144, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 42,148, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 16, 32, 16, 16, 32, 32, 32, 11, 11, - 11, 40,149, 35, 40, 35, 36, 36, 36, 65, 36, 65, 36, 64, 36, 36, - 36, 82, 79, 77, 61, 61, 43, 43, 27, 27, 27, 61,150, 61, 61, 61, - 36, 36, 2, 2, 2, 2, 2, 2, 78, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 78, 78, 78, 78, 78, 78, 78, 78, 43, 43, 43, 43, 43, 2, - 43, 36, 36, 36, 2, 66, 66, 64, 36, 36, 36, 43, 43, 43, 43, 2, - 36, 36, 36, 64, 43, 43, 43, 43, 43, 78, 78, 78, 78, 78, 78, 97, - 36, 64, 78, 43, 43, 78, 43, 78, 97, 2, 2, 2, 2, 2, 2, 80, - 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 64, 63, 36, 36, 36, 36, - 36, 36, 36, 36, 64, 43, 43, 77, 79, 77, 79, 43, 43, 43, 43, 43, - 36, 64, 36, 36, 36, 36, 77, 78, 7, 7, 7, 7, 7, 7, 2, 2, - 63, 36, 36, 71, 61, 82, 77, 36, 65, 43, 65, 64, 65, 36, 36, 43, - 36, 36, 36, 36, 36, 36, 76, 2, 36, 36, 36, 36, 36, 82, 43, 78, - 2, 76,151, 43, 43, 43, 43, 43, 16, 16, 16, 16, 16,103, 40, 40, - 16, 16, 16, 16,100, 41, 41, 41, 36, 82, 79, 78, 77, 97, 79, 43, - 152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153, - 16, 16, 16, 16, 16, 16, 35, 65, 36, 36, 36, 36,154, 36, 36, 36, - 36, 41, 41, 41, 41, 41, 41, 41, 41, 74, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36,132, 36, 36, 36, 36, 36, 36, 36, 71, - 36, 36, 36, 36, 36, 36,150, 61, 2, 2, 2,135,116, 2, 2, 2, - 6,155,156,132,132,132,132,132,132,132,116,135,116, 2,113,157, - 2, 2, 2, 2,139,132,132,116, 2,158, 8, 8, 60, 2, 2, 2, - 36, 36, 36, 36, 36, 36, 36,159, 2, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,115,116, 4, 2, 36, 36, 36, 36, 36, - 63, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, - 20,160, 53, 20, 26, 8,128, 61, 61, 61, 61, 61,161, 59, 61, 61, - 2, 2, 2, 90, 27, 27, 27, 27, 27, 27, 27, 84, 61, 61, 61, 61, - 95, 95,127, 27, 84, 61, 61, 61, 61, 61, 61, 61, 61, 27, 61, 61, - 61, 61, 61, 61, 61, 61, 47, 43,162,162,162,162,162,162,162,162, - 163, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 87, 36, - 138, 36, 36, 36, 36, 95, 95, 95, 36, 36, 36, 36, 36, 36, 36, 58, - 164, 95, 95, 95, 95, 95, 95, 95, 11, 11, 11, 32, 16, 16, 16, 16, - 36, 36, 36, 58, 27, 27, 27, 27, 36, 36, 36, 71,145, 27, 27, 27, - 36, 36, 36,165, 27, 27, 27, 27, 36, 36, 36, 36, 36,165, 27, 27, - 36, 36, 36, 27, 27, 27, 27, 30, 36, 36, 36, 36, 36, 36, 27, 36, - 64, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36,165, 30, 36, 36, 36, 36, 36, 36,165, 27, - 36, 36, 36, 36, 72, 36, 36, 36, 36, 36, 64, 43, 43,163, 27, 27, - 36, 36, 36, 36, 58, 2, 2, 2, 36, 36, 36, 36, 27, 27, 27, 27, - 16, 16, 16, 16, 16, 27, 27, 27, 36, 36, 43, 43, 43, 43, 43, 43, - 7, 7, 7, 7, 7, 36, 36, 63, 11, 11, 11, 11,166, 43, 43,141, - 16, 16, 16, 16, 16, 16, 16, 8, 36, 36, 36, 36, 36, 64,167, 51, - 36, 36, 36, 36, 36, 36, 43, 43, 27, 27, 27, 87, 36, 36, 36, 36, - 163, 27, 30, 2, 2, 2, 2, 2, 36, 43, 43, 2, 2, 2, 2, 2, - 36, 36,165, 27, 27, 27, 27, 27, 79, 81, 36, 36, 36, 36, 36, 36, - 43, 43, 43, 57, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 7, 7, 7, 7, 7, 65, 64, 65, 36, 36, 36, 36, 64, - 78, 79, 43, 77, 79, 57, 73, 2, 2, 43, 43, 43, 43, 43, 67, 59, - 36, 36, 36, 64, 43, 43, 79, 43, 43, 43, 43, 7, 7, 7, 7, 7, - 2, 2, 82, 81, 36, 36, 36, 36, 36, 64, 2, 36, 36, 36, 36, 36, - 36, 82, 78, 43, 43, 43, 43, 77, 81, 36, 58, 2, 56, 43, 57, 79, - 7, 7, 7, 7, 7, 58, 58, 2, 90, 27, 27, 27, 27, 27, 27, 27, - 36, 36, 36, 36, 36, 36, 78, 79, 43, 78, 77, 43, 2, 2, 2, 65, - 36, 36, 36, 36, 36, 36, 36, 64, 77, 78, 78, 78, 78, 78, 78, 78, - 36, 36, 36, 82, 78, 78, 81, 36, 36, 78, 78, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 78, 79, 43, 43, 43, 78, 78, 78, 78, 78, 78, 77, - 65, 65, 2, 2, 2, 2, 2, 2, 56, 43, 43, 43, 43, 43, 43, 43, - 36, 36, 82, 78, 43, 43, 43, 43, 78, 43, 77, 65, 36, 58, 2, 2, - 7, 7, 7, 7, 7, 2, 2, 65, 78, 79, 43, 43, 77, 77, 78, 79, - 77, 43, 36, 66, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82, - 78, 43, 43, 43, 78, 78, 43, 79, 57, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 36, 36, 43, 43, 78, 79, 43, 43, 43, 77, 79, 79, - 57, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 79, 78, - 43, 43, 43, 79, 58, 2, 2, 2, 36, 36, 36, 36, 36, 36, 64, 79, - 78, 43, 43, 79, 43, 43, 43, 43, 7, 7, 7, 7, 7, 27, 2, 89, - 43, 43, 43, 43, 79, 57, 2, 2, 27, 27, 27, 27, 27, 27, 27, 87, - 78, 78, 78, 78, 78, 79, 77, 65, 81, 79, 2, 2, 2, 2, 2, 2, - 82, 78, 43, 43, 43, 43, 78, 78, 65, 66, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 78, 78, 78, 78, 64, 43, 43, 43, 43, 65, 36, 36, - 36, 64, 43, 43, 77, 64, 43, 57, 2, 2, 2, 56, 43, 43, 43, 43, - 64, 43, 43, 77, 79, 43, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, - 43, 43, 43, 77, 43, 2, 66, 2, 58, 2, 2, 2, 2, 2, 2, 2, - 43, 43, 43, 43, 43, 43, 43, 79, 2, 36, 36, 36, 36, 36, 36, 36, - 43, 43, 43, 43, 77, 43, 43, 43, 77, 43, 79, 43, 43, 43, 43, 43, - 43, 43, 43, 64, 43, 43, 43, 43, 36, 36, 36, 36, 36, 78, 78, 78, - 43, 77, 79, 79, 36, 36, 36, 36, 36, 64, 77, 97, 2, 2, 2, 2, - 43, 82, 36, 36, 36, 36, 36, 36, 36, 36, 78, 43, 43, 43, 43, 78, - 77, 57, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 43, 43, 43, - 27, 27, 84, 61, 61, 61, 53, 20,150, 61, 61, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 21, 65, 36, 36, 64, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 78, 79, 43, - 43, 43, 57, 2, 2, 2, 2, 2, 43, 43, 43, 57, 2, 2, 61, 61, - 40, 40, 89, 61, 61, 61, 61, 61, 7, 7, 7, 7, 7,168, 27, 27, - 27, 87, 36, 36, 36, 36, 36, 36, 40, 63, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 76,146, 2, 27, 27, 27, 30, 2, 2, 2, 2, - 82, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 79, - 43, 68, 40, 40, 40, 40, 40, 40, 40, 80, 43, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 47, 57, 61, 61,169, 79, 43, 61,169, 78, - 78,170, 59, 59, 59, 75, 43, 43, 43, 70, 47, 43, 43, 43, 61, 61, - 61, 61, 61, 61, 61, 43, 43, 61, 61, 43, 70, 61, 61, 61, 61, 61, - 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 16, 11, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, - 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, - 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, - 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, - 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 16, 7, - 43, 43, 43, 70, 61, 47, 43, 43, 43, 43, 43, 43, 43, 43, 70, 61, - 61, 61, 47, 61, 61, 61, 61, 61, 61, 61, 70, 21, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 56, 43, 43, 16, 16, 16, 16, 16, 39, 16, 16, - 43, 43, 43, 68, 40, 40, 40, 40, 7, 7, 7, 7, 7, 7, 7, 71, - 7, 7, 7, 7, 7, 7, 7,171, 36, 36, 36, 36, 36, 76, 43, 43, - 172, 7, 7, 7, 7, 7, 7, 85, 16, 16, 43, 43, 43, 68, 40, 40, - 27, 27, 27, 27, 27, 27,145, 27,173, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27,145, 27, 27, 27, 27, 27, 27, 84, 61, - 61, 61, 61, 61, 61, 25, 41, 41, 0, 0, 29, 21, 21, 21, 23, 21, - 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, - 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, - 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, - 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, - 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, - 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, 9, 6, 5, 21, 17, 17, - 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, 12, 21, 7, 21, 1, 1, - 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, - 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 24, 7, 1, 12, - 7, 6, 12, 10, 10, 10, 10, 12, 21, 6, 10, 7, 7, 10, 23, 7, - 15, 26, 13, 21, 13, 7, 15, 7, 12, 23, 21, 26, 21, 15, 17, 7, - 29, 7, 7, 22, 18, 18, 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, - 5, 6, 8, 8, 8, 24, 5, 24, 9, 24, 29, 29, 29, 1, 20, 19, - 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, - 18, 21, 21, 29, 15, 6, 18, 6, 12, 11, 9, 26, 26, 9, 26, 5, - 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, - 18, 22, 5, 12, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, 17, 22, - 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, - 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, - 16, 22, 16, 16, 25, 17, 7, 1, 25, 24, 26, 1, 2, 2, 12, 15, - 21, 14, 7, 15, 9, 12, 12, 17, 13, 15, 26, 10, 10, 1, 13, 23, - 7, 13, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, - 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, - 0, 0, 0, 0, 41, 42, 43, 0, 44, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, - 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, - 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, - 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, - 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, - 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, - 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, - 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, - 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, - 0, 0, 0, 0, 0, 0, 74, 75, 0, 0, 0, 0, 76, 77, 0, 78, - 79, 0, 0, 80, 81, 0, 82, 62, 0, 83, 84, 0, 0, 85, 86, 87, - 0, 88, 0, 89, 0, 90, 0, 0, 51, 91, 51, 0, 92, 0, 93, 0, - 0, 0, 81, 0, 0, 0, 94, 95, 0, 96, 97, 98, 99, 0, 0, 0, - 0, 0, 51, 0, 0, 0, 0,100,101, 0, 0, 0, 0, 0, 0,102, - 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 0, 0,104,105, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, 0, 0, - 0, 0,111, 0,112, 0,105, 0, 0, 0, 0, 0,113,114, 0, 0, - 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, 0,118, - 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, - 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, - 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, - 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, 28, 29, - 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, 33, 0, - 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, - 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 43, - 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, - 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, - 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 0, - 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, 52, 0, - 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, 0, 68, - 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, 0, 0, - 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, 0, 0, - 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, 52, 0, - 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, 57, 0, - 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, 0, 91, - 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, 0, 0, - 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, - 99, 0, 0, 0,100, 0, 0, 0, 0,101,102, 93, 0, 0,103, 0, - 0, 0, 84, 0, 0,104, 0, 0, 0,105,106, 0, 0,107,108, 0, - 0, 0, 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, 33, 0, - 112,113,114, 57, 0, 0,115, 35, 0, 0,116, 0, 0, 0,117, 0, - 0, 0, 0, 0, 0,118, 0, 0,119, 0, 0, 0, 0,120, 88, 0, - 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,121, 0, 0, 0, 0,122, - 0, 0,123, 0, 0, 0, 0,121, 0, 0,124, 0, 0, 0, 0, 0, - 79, 0, 0, 0, 0,125, 0, 0, 0,126, 0, 0, 0,127, 0,128, - 0, 0, 0, 0,129,130,131, 0,132, 0,133, 0, 0, 0,134,135, - 136, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,137, 0, 0, 0, - 138, 0, 0, 0,139, 0, 0,140, 0, 0,141, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, 4, 8, - 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, 19, 1, - 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, 29, 30, - 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, 37, 0, - 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, 43, 36, - 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, 0, 38, - 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, 52, 1, 0, 0, - 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, 0, 0, - 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, 0, 60, - 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, 0, 0, - 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 69, - 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, 0, 78, - 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, 0, 62, - 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 19, - 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, 36, 10, - 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, - 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 87, 9, - 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, 93, 1, - 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, 58, 0, - 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0, - 101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, - 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, 78, 0, - 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, 1, 14, - 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, 0, 0, - 109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, - 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, 62, 0, - 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, - 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, 0, 38, - 1, 58, 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,115, 0, - 0, 0, 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, - 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, - 0, 0, 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, - 0,117, 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, - 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, - 87, 0, 0, 0, 0, 1, 0, 0, 0,123, 0, 0, 0,112, 4,122, - 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230,230,232, - 220,220,220,220,232,216,220,220,220,220,220,202,202,220,220,220, - 220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220,220,230, - 230,230,230,240,230,220,220,220,230,230,230,220,220, 0,230,230, - 230,220,220,220,220,230,232,220,220,230,233,234,234,233,234,234, - 233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230,230,230, - 222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0,230,220, - 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, - 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, 0, 0, - 230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, 0, 36, - 0, 0,230,220,230,230,220,220,230,220,220,230,220,230,220,230, - 230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230,230,230, - 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, 27, 28, - 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230,230, 0, - 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, - 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107,118,118, - 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, 0,216, - 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, - 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, 0, 0, - 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, 0,228, - 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220,230,220, - 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0,230, 0, - 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230,230,230, - 232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, 1, 1, - 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228,232,222, - 224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230,220, 0, - 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, 0,230, - 220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, 0, 7, - 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, 0,216, - 216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0,220,220, - 220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 33, 17, 49, - 17, 17, 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, - 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, - 3, 3, 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, - 3, 15, 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, - 3, 22, 23, 24, 3, 3, 3, 3, 3, 3, 25, 3, 3, 3, 3, 3, - 3, 3, 3, 26, 3, 3, 27, 28, 0, 1, 0, 0, 0, 0, 0, 1, - 0, 2, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, - 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, - 0, 0, 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, - 19, 19, 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, - 29, 30, 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, - 28, 0, 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, - 0, 41, 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, - 14, 14, 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, - 47, 47, 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, - 0, 54, 6, 55, 0, 14, 19, 1, 0, 0, 0, 0, 56, 57, 0, 0, - 0, 0, 0, 19, 58, 31, 0, 0, 0, 0, 0, 0, 0, 59, 14, 0, - 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 60, 61, 0, - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 3, 0, 4, - 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 1, 1, 0, 0, 8, - 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, 10, 11, 12, 0, 0, 0, - 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, 0, 1, 0, 0, 18, - 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, - 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, 22, 0, 0, 0, 0, 1, - 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, 8, 21, 27, 0, 1, 0, - 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, 0, 31, 32, 20, 1, 1, - 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, 0, 0, 33, 9, 0, 1, - 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, 35, 21, 21, 21, 9, 36, - 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, 1, 0, 1, 0, 0, 0, - 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, 8, 21, 21, 21, 21, 21, - 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, 21, 21, 21, 9, 0, 0, - 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, 0, 0, 0, 45, 8, 9, - 1, 0, 0, 0, 8, 21, 21, 21, 9, 0, 1, 0, 1, 1, 8, 21, - 21, 9, 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 3, 3, 3, 3, 3, 3, - 3, 15, 3, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 0, 0, 1, 2, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 17, 17, - 18, 17, 19, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 25, 25, 26, 27, - 28, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 31, - 31, 31, 31, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 57, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 58, 31, 31, 31, - 59, 60, 61, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 63, 64, 65, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 66, 67, 68, 31, 31, 31, 31, 69, 31, 31, 31, 31, 31, - 31, 31, 17, 70, 71, 72, 17, 17, 73, 74, 31, 75, 76, 77, 78, 79, - 80, 31, 81, 82, 17, 83, 17, 17, 17, 17, 31, 31, 23, 23, 23, 23, - 23, 23, 23, 84, 31, 31, 31, 31, 23, 84, 31, 31, 23, 23, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 85, 0, 0, 1, - 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, - 4, 4, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 11, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 19, 27, - 28, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, - 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 42, 43, 44, 44, 45, 46, - 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 49, 50, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 51, - 60, 61, 62, 63, 64, 65, 66, 7, 67, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 7, 4, 4, 4, 4, 77, 77, 77, 77, 78, 79, 80, 81, - 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 85, 85, 85, - 0, 0, 0, 0, 86, 87, 88, 88, 89, 90, 48, 91, 0, 0, 92, 92, - 92, 92, 92, 93, 94, 95, 96, 97, 98, 47, 99,100,101,102, 0,103, - 104,105, 0, 0, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - 92, 92, 92, 0,106,106,106,106,106,106,106,106,106,106,106,107, - 108,108,108,108,108, 11,109,110,111, 4,112, 4,113,114,115,116, - 117,118,119,120,121,122,123,124,125,126, 50,127, 47, 47, 47, 47, - 47, 47, 47, 47,128,128,128,128,128,128,128,128,128,128,128,128, - 92, 92, 92, 92, 92, 92, 92, 92,129,130, 19, 19, 19, 19, 19, 19, - 131, 19, 19, 19,132,133, 19,134,135,136,137,101,138,138,138,138, - 0, 77,139,140,128,128,141,142,143,144,145,146,147,148,149,150, - 151,152,153,154,155,155,155,155,155,155, 4, 4,156,157,158,159, - 160,161,162,163,164,165,166,167,168,169,170,170,171,171,172,172, - 173,174,174,174, 19, 19,175,176,177,178,179,180,181,181,182,183, - 184,185,186,187,188,188,189,190,191,192,193,193,194,194,195,195, - 128,128,196,196,197,198,199,200,201,201,128,128,202,202,203,203, - 204,204,205,205,206,207,208,209, 28, 28,210,210,211,212,213,213, - 214,215,216,216,128,128,217,217,218,218,219, 34,220,220,220,220, - 220,220,220,220,220,220,220,220,220,220,128,128,128,128,128,128, - 128,128,221,221,222,222,222,222,222,222,222,222,223,223,223,223, - 223,223,223,223,223,223,128,128,128,128,128,128,128,128,128,128, - 224,224,128,128,110,110,110,110,110,110,110,110,110,225,226,227, - 228,228,228,228,128,128,128,128,229,229,128,128,230,230,230,230, - 231,231,231,232,233,233,233,233,233,233,233,233,233,233,233,233, - 234,234,234,234,234,234,234,234,233,233,128,128,128,128,128,128, - 128,128,104,104,235,236,236,236,237,238,239,239,239,239,239,239, - 128,128,128,128,240,240,241, 0,128,128,128,128, 0, 0, 0, 0, - 7,242, 0, 0, 0, 0, 0, 0, 0,243,244, 0, 77, 77, 0, 0, - 0, 0,128,128,245,245,245,245,245,245,245,245,245,245,245,245, - 128,128,128,128,128,128,128,128, 4, 4,128,128,246, 11, 11, 11, - 247,247,128,128,128,128,248,249,128,128,128,128,128,128,250,250, - 128,128,251,251,128,128,128,128,128,128, 48, 48,252,252,252,252, - 253,253,128,128, 0, 0, 0, 0, 0, 0,128,128, 19, 19, 19, 19, - 128,128,128,128,254, 0,128,128, 0, 0, 0, 0, 92, 92,128,128, - 128,128,128,128, 0, 0,128,128, 7, 7, 7, 7, 0, 0, 0, 0, - 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, 4, 4, 4, - 4, 4, 4, 6, 0, 0, 7, 0, 8, 8, 8, 8, 8, 8, 8, 9, - 10, 11, 11, 11, 11, 11, 12, 11, 13, 13, 13, 13, 14, 13, 13, 13, - 13, 13, 13, 15, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, - 19, 20, 21, 21, 22, 23, 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, - 26, 26, 26, 26, 26, 21, 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, - 29, 29, 29, 29, 30, 30, 26, 21, 21, 21, 31, 21, 32, 32, 32, 32, - 32, 33, 34, 32, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, - 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, - 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 45, 44, 44, 44, 44, - 46, 46, 46, 46, 47, 47, 47, 47, 47, 48, 47, 47, 49, 49, 49, 49, - 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, 52, 52, 53, 53, 53, 53, - 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, - 57, 57, 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, 64, 64, - 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 55, 67, 67, 67, 67, - 67, 68, 68, 68, 69, 69, 69, 69, 69, 69, 64, 64, 70, 70, 71, 71, - 71, 71, 71, 71, 71, 71, 71, 8, 72, 72, 72, 72, 73, 73, 73, 73, - 74, 74, 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, 50, 50, 50, - 73, 77, 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, 4, 4, 84, - 11, 11, 11, 11, 85, 0, 0, 0, 0, 0, 0, 86, 0, 4, 0, 0, - 0, 8, 8, 8, 0, 0, 87, 88, 89, 0, 4, 4, 6, 0, 0, 0, - 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 4, 4, 92, 92, 92, 92, - 50, 50, 50, 93, 93, 93, 93, 93, 53, 53, 13, 13, 94, 94, 94, 94, - 94, 94, 94, 0, 95, 0, 96, 97, 98, 99, 99, 99, 99,100,101,102, - 102,102,102,103,104,104,104,105, 52, 0,104,104, 0, 0, 0,102, - 52, 52, 0, 0, 0, 0, 52,106, 0,102,102,107,102,102,102,102, - 102,108, 0, 0,109,109,109,109,109,110,110,110,111,111,111,111, - 13, 13,112,112,112,112,112,112, 0, 0,113, 4,114, 4, 4, 4, - 115,115,115, 0,116,116,116,116,117,117,117,117,117,117, 32, 32, - 118,118,119,120,120,120, 52, 52,121,121,121,121,122,121, 49, 49, - 123,123,123,123,123,123, 49, 49,124,124,124,124,124,124,125,125, - 53, 53, 53, 4, 4,126,127, 54,125,125,125,125,128,128,128,128, - 4,129, 18, 18, 18, 21, 21, 21, 21, 21, 21,130, 8, 0,131, 0, - 0, 0, 0, 21, 21, 21, 21,132, 0, 0, 1, 2, 1, 2,133,101, - 102,134, 52, 52,135,135,135,135, 11, 0, 11, 11, 11, 0, 0,136, - 137,137,138,138,138,138,139, 0,140,140,140,141,141,142,142,142, - 143,143,144,144,144,144,144,144,145,145,145,145,145,146,146,146, - 147,147,147,148,148,148,148,148,149,149,149,150,150,150,150,151, - 151,151,151,151,152,152,152,152,153,153,153,153,154,154,154,154, - 155,155,156,156,157,157,157,157,157,157,158,158,159,159,160,160, - 160,160,160,160,161,161,162,162,162,162,162,162,163,163,163,163, - 163,163,164,164,165,165,165,165,166,166,166,166,167,167,167,167, - 168,168,169,169,170,170,170,170,171,171,171,171,172,172,172,172, - 173,173,173,173,174,174,174,174,175,175,175,175,176, 21, 21, 21, - 177,177,177,178,178,178,178,179,179,179,179,180,180,180,181,181, - 182,182,182,182,183,183,183,183,183,184,184,184,185,185,185,185, - 185,186,186,186,187,187,187,187,187,187,188, 43,189,189,189,189, - 190,190,190,191,191,191,191,191,192,192,192,193,192,192,192,192, - 194,194,194,194,195,195,195,195,196,196,196,196,197,197,197,197, - 198,198,198,198,198,198, 66, 66,199,199,199,199,199, 49, 49, 49, - 200,200,200,200,201,201,201,201,202,202,202,202,203,203,203,203, - 204,204,204,204,205,205,205,205,205,206,206,206,206,206,206, 55, - 207,207,207,207,208,208,208,208,209,209,209,209,209,209,209,210, - 210,210,210,210,211,211,211,211,211,211,212,212,212,212,212,212, - 213,213,213,213,214,214,214,214,110,110,110,110,215,215,215,215, - 216,216,216,216,217,217,217,217,218,218,218,218,219,219,219,219, - 220,220,220,221,221,221,221,221,221,222,222,222,223,223,223,223, - 224,224,224,224,225,225,225,225,226,226,226,226,226,226,227, 94, - 228,228,228,228,229,229,229,229,230, 99, 99, 99, 99, 99, 99, 99, - 99, 99,102,231, 99,232,102,233,233,233,233,233,234,234,234,234, - 234,234, 0, 0, 8, 0, 0, 0, 0, 0,235,236,237, 0,238, 0, - 239,239,239,239, 91, 91, 91, 13,240,240,240,240,241,241,241,241, - 242,242,242,242,243,243,243,243,244,244,244,244,245,245,245,245, - 246,246,246,246,247, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, - 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, - 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 8, 10, - 8, 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, 14, 14, - 14, 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, 19, 19, - 19, 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, 20, 20, - 22, 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, 20, 21, - 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, - 31, 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, 33, 33, - 33, 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, - 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, - 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, - 48, 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, 52, 52, - 53, 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, 56, 56, - 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, - 60, 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 0, 0, - 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, 71, 71, - 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, - 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, - 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, 7, 7, - 83, 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, 2, 89, - 90, 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, 0, 86, - 1, 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, 0, 4, - 97, 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, 99, 99, - 100,100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0,100, 0, - 105,106,106,106,106,106,106,106,106,106,107,105,108,109,109,109, - 109,109,109,109,109,109,110,108,111,111,111,111,112, 55, 55, 55, - 55, 55, 55,113,109,109,109,110,109,109, 0, 0,114,114,114,114, - 115,115,115,115,116,116,116,116,117,117,117,117, 96, 2, 2, 2, - 2, 2, 94, 2,118,118,118,118,119,119,119,119,120,120,120,120, - 121,121,121,121,121,121,121,122,123,123,123,123,124,124,124,124, - 124,124,124,125,126,126,126,126,127,127,127,127,128,128,128,128, - 2, 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, 17, 18, - 20, 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135,109,109, - 109,109,109,136,137,137,137,137, 0, 0, 0,138,139,139,139,139, - 140,140,140,140, 84, 0, 0, 0,141,141,141,141,142,142,142,142, - 143,143,143,143,144,144,144,144,145,145,145,145,146,146,146,146, - 147,147,147,147,148,148,148,148,149,149,149,149,150,150,150,150, - 151,151,151,151,152,152,152,152,153,153,153,153,154,154,154,154, - 155,155,155,155,156,156,156,156,157,157,157,157,158,158,158,158, - 159,159,159,159,160,160,160,160,161,161,161,161,162,162,162,162, - 163,163,163,163,164,164,164,164,165,165,165,165,166,166,166,166, - 167,167,167,167,168,168,168,168,169,169,169,169,170,170,170,170, - 171,171,171,171,172,172,172,172,173,173,173,173,174,174,174,174, - 175,175,175,175,176,176,176,176,177, 20, 20, 20,178,178,178,178, - 179,179,179,179,180,180,180,180,181,181,181,181,182,182,182,182, - 183,183,183,183,184,184,184,184,185,185,185,185,186,186,186,186, - 187,187,187,187,188,188,188,188,189, 45, 45, 45,190,190,190,190, - 191,191,191,191,192,192,192,192,193,193,193,193,193,193,194,193, - 195,195,195,195,196,196,196,196,197,197,197,197,198,198,198,198, - 199,199,199,199,200,200,200,200,201,201,201,201,202,202,202,202, - 203,203,203,203,204,204,204,204,205,205,205,205,206,206,206,206, - 207,207,207,207,208,208,208,208,209,209,209,209,210,210,210,210, - 211,211,211,211,212,212,212,212,213,213,213,213,214,214,214,214, - 215,215,215,215,216,216,216,216,217,217,217,217,218,218,218,218, - 219,219,219,219,220,220,220,220,221,221,221,221,222,222,222,222, - 223,223,223,223,224,224,224,224,225,225,225,225,226,226,226,226, - 227,227,227,227,228,229,229,229,230,230,230,230,229,229,229,229, - 231,106,106,106,232,106,106,106,106,233,109,109,234,234,234,234, - 235,235,235,235, 0,236, 86, 0, 0, 0,236, 7, 82,138, 7, 0, - 0, 0,237, 86,238,238,238,238,239,239,239,239,240,240,240,240, - 241,241,241,241,242,242,242,242,243,243,243,243,244,244,244,244, - 245,245,245,245,246, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, - 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, - 9, 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, 55, 55, 55, 55, - 6, 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, 4, 4, 4, 4, - 4, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 0, 3, 3, - 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, 3, 3, - 1, 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, 64, 64, 64, 64, - 90, 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, 7, 7, 7, 7, - 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, - 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 22, 22, 22, 22, - 23, 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, 36, 36, - 24, 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, 25, 25, 25, 25, - 25, 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, 8, 8, 8, 8, - 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 29, 29, 29, 29, - 28, 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 0, - 0, 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, 44, 0, 0, 0, - 43, 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, 32, 32, 0, 0, - 32, 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, 52, 52, 52, 52, - 58, 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, 62, 62, 62, 62, - 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 73, 73, 73, 73, - 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, - 1, 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, 9, 9, - 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, - 0, 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, - 56, 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, 0, 13, 0, 13, - 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 0, 15, 15, 15, - 15, 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, 12, 0, - 39, 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, - 60, 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, 69, 69, 69, 69, - 69, 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, 84, 84, 84, 0, - 68, 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, 19, 9, 19, 19, - 19, 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, 3, 3, 0, 0, - 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, 49, 49, 49, 49, - 0, 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, 42, 42, 42, 42, - 41, 41, 41, 41,118,118,118,118, 53, 53, 53, 53, 59, 59, 59, 59, - 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50,135,135,135,135, - 106,106,106,106,104,104,104,104,161,161,161,161,170,170,170,170, - 110,110,110,110, 47, 47, 47, 47, 81, 81, 81, 81,120,120,120,120, - 116,116,116,116,128,128,128,128, 66, 66, 66, 66, 72, 72, 72, 72, - 98, 98, 98, 98, 97, 97, 97, 97, 57, 57, 57, 57, 88, 88, 88, 88, - 117,117,117,117,112,112,112,112, 78, 78, 78, 78, 83, 83, 83, 83, - 82, 82, 82, 82,122,122,122,122, 89, 89, 89, 89,130,130,130,130, - 144,144,144,144,165,165,165,165,156,156,156,156,156,156, 3, 3, - 147,147,147,147,148,148,148,148,158,158,158,158,153,153,153,153, - 149,149,149,149, 94, 94, 94, 94, 85, 85, 85, 85,101,101,101,101, - 96, 96, 96, 96,111,111,111,111,100,100,100,100,100, 36, 36, 36, - 108,108,108,108,129,129,129,129,109,109,109,109,107,107,107,107, - 107,107,107, 1,171,171,171,171,137,137,137,137,124,124,124,124, - 123,123,123,123,114,114,114,114,102,102,102,102,126,126,126,126, - 142,142,142,142,125,125,125,125,154,154,154,154,150,150,150,150, - 141,141,141,141,140,140,140,140,121,121,121,121,169,169,169,169, - 133,133,133,133,134,134,134,134,138,138,138,138,143,143,143,143, - 145,145,145,145,163,163,163,163, 63, 63, 63, 63,157,157,157,157, - 80, 80, 80, 80,127,127,127,127,166,166,166,166,115,115,115,115, - 159,159,159,159,103,103,103,103,119,119,119,119,167,167,167,167, - 146,146,146,146, 99, 99, 99, 99,136,139, 13, 13,155,155,155,155, - 136,136,136,136, 17, 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17, - 139,139,139,139,105,105,105,105, 0, 0, 0, 1, 0, 0, 1, 1, - 131,131,131,131,151,151,151,151,160,160,160,160,152,152,152,152, - 164,164,164,164,168,168,168,168,113,113,113,113,132,132,132,132, - 15, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, - 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 16, 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, - 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, - 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, - 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, - 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, - 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, - 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, - 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, - 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100, - 101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, - 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0, - 115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, - 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,128,129,130,131,132,133,134,135,136,137,138,139,140,141, - 142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, - 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162, 0, - 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, 0, 0, - 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0,170, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178,179, 0, - 0, 0,180,181,182,183,184,185,186,187,188,189,190,191,192,193, - 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, - 210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, - 3, 4, -}; -static const uint16_t -_hb_ucd_u16[5080] = -{ - 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, - 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, - 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, - 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, - 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, - 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, - 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, - 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, - 47, 64, 65, 66, 47, 67, 47, 47, 68, 69, 47, 47, 70, 32, 71, 32, - 72, 47, 47, 73, 74, 75, 76, 77, 78, 47, 47, 79, 80, 81, 82, 83, - 84, 47, 47, 85, 86, 87, 88, 89, 84, 47, 47, 79, 90, 47, 82, 91, - 92, 47, 47, 93, 94, 95, 82, 96, 97, 47, 47, 98, 99, 100, 101, 102, - 103, 47, 47, 104, 105, 106, 82, 107, 108, 47, 47, 93, 109, 110, 82, 111, - 112, 47, 47, 113, 114, 115, 82, 116, 92, 47, 47, 47, 117, 118, 101, 119, - 47, 47, 47, 120, 121, 122, 66, 66, 47, 47, 47, 123, 124, 125, 47, 47, - 126, 127, 128, 129, 47, 47, 47, 130, 131, 32, 32, 132, 133, 134, 66, 66, - 47, 47, 135, 136, 122, 137, 138, 139, 140, 141, 9, 9, 9, 11, 11, 142, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 143, 144, 145, - 47, 146, 9, 9, 9, 9, 9, 147, 148, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 149, 47, 150, 151, 47, 47, 47, 47, 152, 153, - 47, 154, 47, 155, 47, 156, 47, 156, 47, 47, 47, 157, 158, 159, 160, 145, - 161, 160, 47, 47, 162, 47, 47, 47, 163, 47, 164, 47, 47, 47, 47, 47, - 47, 47, 165, 166, 167, 47, 47, 47, 47, 47, 47, 47, 47, 168, 146, 146, - 47, 169, 47, 47, 47, 170, 171, 172, 160, 160, 173, 174, 32, 32, 32, 32, - 175, 47, 47, 176, 177, 122, 178, 179, 180, 47, 181, 61, 47, 47, 182, 183, - 47, 47, 184, 185, 186, 61, 47, 187, 188, 9, 9, 9, 66, 189, 190, 191, - 11, 11, 192, 27, 27, 27, 193, 194, 11, 195, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 196, 13, 13, 13, 13, 13, 13, - 197, 197, 197, 197, 197, 198, 197, 11, 199, 199, 199, 200, 201, 202, 202, 201, - 203, 204, 205, 206, 207, 208, 209, 210, 211, 27, 212, 212, 212, 213, 214, 32, - 215, 216, 217, 218, 219, 145, 220, 220, 221, 222, 223, 146, 224, 225, 146, 226, - 227, 227, 227, 227, 227, 227, 227, 227, 228, 146, 229, 146, 146, 146, 146, 230, - 146, 231, 227, 232, 146, 233, 234, 146, 146, 146, 146, 146, 146, 146, 145, 145, - 145, 235, 146, 146, 146, 146, 236, 145, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 237, 238, 146, 146, 239, 146, 146, 146, 146, 146, 146, 240, 146, - 146, 146, 146, 146, 146, 146, 241, 242, 145, 243, 146, 146, 244, 227, 245, 227, - 246, 247, 227, 227, 227, 248, 227, 249, 146, 146, 146, 227, 250, 146, 146, 146, - 9, 9, 9, 11, 11, 11, 251, 252, 13, 13, 13, 13, 13, 13, 253, 254, - 11, 11, 11, 47, 47, 47, 255, 256, 47, 47, 47, 47, 47, 47, 32, 32, - 257, 258, 259, 260, 261, 262, 263, 263, 264, 265, 266, 267, 268, 47, 47, 47, - 47, 269, 148, 47, 47, 47, 47, 270, 47, 271, 47, 47, 146, 146, 146, 47, - 146, 146, 272, 146, 273, 274, 146, 146, 272, 146, 146, 274, 146, 146, 146, 146, - 47, 47, 47, 47, 146, 146, 146, 146, 47, 275, 47, 47, 47, 47, 47, 47, - 47, 146, 146, 146, 146, 47, 47, 187, 276, 47, 61, 47, 13, 13, 277, 278, - 13, 279, 47, 47, 47, 47, 280, 281, 31, 282, 283, 284, 13, 13, 13, 285, - 286, 287, 288, 289, 290, 291, 9, 292, 293, 47, 294, 295, 47, 47, 47, 296, - 297, 47, 47, 298, 299, 160, 32, 300, 61, 47, 301, 47, 302, 303, 47, 47, - 72, 47, 47, 304, 305, 306, 307, 61, 47, 47, 308, 309, 310, 311, 47, 312, - 47, 47, 47, 313, 58, 314, 315, 316, 47, 47, 47, 11, 11, 317, 318, 11, - 11, 11, 11, 11, 47, 47, 319, 160, 320, 320, 320, 320, 320, 320, 320, 320, - 321, 321, 321, 321, 321, 321, 321, 321, 11, 322, 323, 47, 47, 47, 47, 47, - 47, 47, 47, 324, 31, 325, 47, 47, 47, 47, 47, 326, 146, 47, 47, 47, - 47, 47, 47, 47, 327, 146, 146, 328, 32, 329, 32, 330, 331, 332, 333, 47, - 47, 47, 47, 47, 47, 47, 47, 334, 335, 2, 3, 4, 5, 336, 337, 338, - 47, 339, 47, 47, 47, 47, 340, 341, 342, 145, 145, 343, 220, 220, 220, 344, - 345, 146, 146, 146, 146, 146, 146, 346, 347, 347, 347, 347, 347, 347, 347, 347, - 47, 47, 47, 47, 47, 47, 348, 145, 47, 47, 349, 47, 350, 47, 47, 60, - 47, 351, 47, 47, 47, 352, 220, 220, 9, 9, 147, 11, 11, 47, 47, 47, - 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 351, 9, - 9, 353, 11, 11, 47, 47, 47, 47, 27, 27, 27, 27, 27, 27, 27, 27, - 47, 47, 47, 47, 47, 354, 47, 355, 47, 47, 356, 145, 145, 145, 47, 357, - 47, 358, 47, 351, 66, 66, 66, 66, 47, 47, 47, 359, 145, 145, 145, 145, - 360, 47, 47, 361, 145, 66, 47, 362, 47, 363, 145, 145, 364, 47, 365, 66, - 47, 47, 47, 366, 47, 367, 47, 367, 47, 366, 144, 145, 145, 145, 145, 145, - 9, 9, 9, 9, 11, 11, 11, 368, 47, 47, 369, 160, 370, 9, 371, 11, - 372, 227, 227, 227, 227, 227, 227, 227, 145, 145, 145, 145, 145, 145, 145, 145, - 47, 47, 373, 47, 47, 47, 47, 374, 47, 363, 375, 47, 60, 376, 66, 47, - 377, 66, 66, 47, 378, 145, 47, 47, 379, 47, 47, 361, 380, 381, 382, 383, - 180, 47, 47, 384, 385, 47, 47, 160, 97, 47, 386, 387, 388, 47, 47, 389, - 180, 47, 47, 390, 391, 392, 393, 145, 47, 47, 394, 395, 360, 32, 32, 32, - 47, 47, 366, 47, 47, 396, 172, 160, 92, 47, 47, 113, 397, 398, 399, 32, - 47, 47, 47, 400, 401, 402, 403, 32, 47, 47, 47, 404, 405, 406, 47, 47, - 47, 47, 47, 407, 408, 160, 160, 160, 47, 47, 409, 410, 411, 412, 32, 32, - 47, 47, 47, 413, 414, 160, 66, 66, 47, 47, 415, 416, 160, 160, 160, 160, - 47, 417, 418, 419, 47, 47, 47, 47, 47, 47, 394, 420, 66, 66, 66, 66, - 9, 9, 9, 9, 11, 11, 128, 421, 47, 47, 47, 422, 423, 160, 160, 160, - 47, 47, 47, 47, 47, 424, 425, 426, 427, 47, 47, 428, 429, 430, 47, 47, - 431, 432, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 66, 66, - 47, 47, 47, 47, 47, 47, 433, 160, 47, 47, 409, 434, 433, 128, 145, 435, - 47, 156, 436, 437, 32, 32, 32, 32, 47, 47, 47, 360, 438, 160, 47, 47, - 439, 440, 160, 160, 160, 160, 160, 160, 47, 47, 47, 47, 47, 47, 47, 441, - 442, 47, 47, 443, 444, 445, 32, 32, 47, 47, 47, 47, 145, 446, 447, 448, - 220, 220, 220, 220, 220, 220, 220, 66, 47, 47, 47, 47, 47, 47, 47, 433, - 47, 47, 47, 209, 449, 32, 47, 47, 47, 450, 451, 160, 160, 160, 160, 160, - 47, 47, 47, 47, 47, 47, 306, 47, 47, 47, 47, 47, 160, 47, 47, 452, - 47, 47, 47, 453, 454, 455, 456, 47, 27, 27, 27, 27, 457, 47, 458, 160, - 9, 9, 9, 9, 9, 9, 11, 11, 145, 459, 66, 66, 66, 66, 66, 66, - 47, 47, 47, 47, 396, 460, 426, 426, 461, 462, 27, 27, 27, 27, 463, 426, - 47, 464, 209, 209, 209, 209, 209, 209, 146, 146, 146, 146, 146, 146, 146, 160, - 32, 32, 32, 32, 32, 146, 146, 146, 146, 146, 146, 146, 146, 146, 465, 466, - 467, 146, 468, 146, 146, 146, 146, 146, 146, 146, 146, 146, 469, 146, 146, 146, - 9, 470, 11, 471, 472, 11, 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, - 471, 472, 11, 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, 471, 472, 11, - 197, 9, 473, 474, 9, 475, 11, 9, 470, 11, 197, 9, 476, 477, 478, 479, - 11, 480, 9, 481, 482, 483, 484, 11, 485, 9, 486, 11, 487, 160, 160, 160, - 32, 32, 32, 488, 32, 32, 489, 490, 491, 492, 32, 32, 32, 32, 32, 32, - 493, 11, 11, 11, 11, 11, 11, 11, 32, 32, 32, 27, 27, 27, 27, 27, - 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 494, 495, 146, 146, 146, - 47, 47, 450, 32, 47, 47, 374, 496, 47, 47, 47, 47, 47, 47, 497, 160, - 47, 47, 47, 47, 47, 47, 450, 498, 47, 47, 47, 47, 356, 32, 32, 32, - 9, 9, 473, 11, 499, 306, 66, 66, 145, 145, 500, 501, 145, 145, 145, 145, - 145, 145, 502, 145, 145, 145, 145, 145, 47, 47, 47, 47, 47, 47, 47, 227, - 503, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 504, - 209, 209, 209, 209, 209, 209, 209, 209, 0, 0, 0, 0, 0, 0, 0, 0, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 716, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 717, + 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 12, 11, 11, 11, 13, 11, + 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 16, 17, 18, 17, 17, 19, 20, 21, 21, 22, 21, 23, 24, + 25, 26, 27, 27, 28, 29, 27, 30, 27, 27, 27, 27, 27, 31, 27, 27, + 32, 33, 33, 33, 34, 27, 27, 27, 35, 35, 35, 36, 37, 37, 37, 38, + 39, 39, 40, 41, 42, 43, 44, 27, 27, 45, 27, 27, 27, 27, 46, 27, + 47, 47, 47, 47, 47, 48, 49, 47, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 108, 109, 110, 111, 108, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 121, 122, 121, 123, 124, 124, 125, 126, 127, 128, 129, 130, 124, 124, + 131, 131, 131, 131, 132, 131, 133, 134, 131, 132, 131, 135, 135, 136, 124, 124, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 138, 138, 139, 138, 138, 140, + 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 143, 144, 142, 142, + 143, 142, 142, 145, 146, 147, 142, 142, 142, 146, 142, 142, 142, 148, 142, 149, + 142, 150, 151, 151, 151, 151, 151, 152, 153, 153, 153, 153, 153, 153, 153, 153, + 154, 155, 156, 156, 156, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 167, 167, 167, 167, 168, 169, 169, 170, 171, 172, 172, 172, 172, 172, 173, + 172, 172, 174, 153, 153, 153, 153, 175, 176, 177, 178, 178, 179, 180, 181, 182, + 183, 183, 184, 183, 185, 186, 167, 167, 187, 188, 189, 189, 189, 190, 189, 191, + 192, 192, 193, 8, 8, 194, 195, 124, 196, 196, 196, 196, 197, 196, 196, 196, + 198, 198, 198, 198, 199, 199, 199, 200, 201, 201, 201, 202, 203, 204, 204, 204, + 205, 138, 138, 206, 207, 208, 209, 210, 4, 4, 211, 4, 4, 212, 213, 214, + 4, 4, 4, 215, 8, 8, 8, 8, 11, 216, 11, 11, 216, 217, 11, 218, + 11, 11, 11, 219, 219, 220, 11, 221, 222, 0, 0, 0, 0, 0, 223, 224, + 225, 226, 0, 0, 227, 8, 8, 228, 0, 0, 229, 230, 231, 0, 4, 4, + 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 233, 124, 234, 124, 0, 0, 235, 235, 235, 235, 235, 235, 235, 235, + 0, 0, 0, 0, 0, 0, 0, 236, 237, 237, 237, 237, 237, 237, 4, 4, + 238, 238, 238, 238, 238, 238, 238, 239, 138, 138, 139, 240, 240, 240, 241, 242, + 142, 243, 244, 244, 244, 244, 14, 14, 0, 0, 0, 0, 0, 245, 124, 124, + 246, 247, 246, 246, 246, 246, 246, 248, 246, 246, 246, 246, 246, 246, 246, 246, + 246, 246, 246, 246, 246, 249, 124, 0, 250, 0, 251, 252, 253, 254, 254, 254, + 254, 255, 256, 257, 257, 257, 257, 258, 259, 260, 260, 261, 141, 141, 141, 141, + 262, 0, 260, 260, 0, 0, 263, 257, 141, 262, 0, 0, 0, 0, 141, 264, + 0, 0, 0, 0, 0, 257, 257, 265, 257, 257, 257, 257, 257, 266, 0, 0, + 246, 246, 246, 246, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, + 268, 267, 267, 267, 269, 270, 270, 270, 271, 271, 271, 271, 271, 271, 271, 271, + 271, 271, 272, 124, 14, 14, 14, 14, 14, 14, 273, 273, 273, 273, 273, 274, + 0, 0, 275, 4, 4, 4, 4, 4, 276, 4, 4, 4, 4, 226, 124, 277, + 278, 278, 279, 233, 280, 280, 280, 281, 282, 282, 282, 282, 283, 284, 47, 47, + 285, 285, 286, 287, 287, 288, 141, 289, 290, 290, 290, 290, 291, 292, 137, 293, + 294, 294, 294, 295, 296, 297, 137, 137, 298, 298, 298, 298, 299, 300, 301, 302, + 303, 304, 244, 4, 4, 305, 306, 151, 151, 151, 151, 151, 301, 301, 307, 308, + 141, 141, 309, 141, 310, 141, 141, 311, 124, 124, 124, 124, 124, 124, 124, 124, + 246, 246, 246, 246, 246, 246, 312, 246, 246, 246, 246, 246, 246, 313, 124, 124, + 314, 315, 21, 316, 317, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 318, 27, 27, 27, 27, 27, 27, 27, 27, 27, 124, 124, 27, + 8, 233, 319, 0, 0, 320, 321, 322, 27, 27, 27, 27, 27, 27, 27, 323, + 324, 0, 1, 2, 1, 2, 325, 256, 257, 326, 141, 262, 327, 328, 329, 330, + 331, 332, 333, 334, 335, 335, 124, 124, 332, 332, 332, 332, 332, 332, 332, 336, + 337, 0, 0, 338, 11, 11, 11, 11, 339, 340, 341, 124, 124, 0, 0, 342, + 343, 344, 345, 345, 345, 346, 347, 348, 349, 349, 350, 351, 352, 353, 353, 354, + 355, 356, 357, 357, 358, 359, 124, 124, 360, 360, 360, 360, 360, 361, 361, 361, + 362, 363, 364, 365, 365, 366, 365, 367, 368, 368, 369, 370, 370, 370, 371, 372, + 372, 373, 374, 375, 376, 376, 376, 377, 378, 378, 378, 378, 378, 378, 378, 378, + 378, 378, 378, 379, 378, 380, 381, 124, 382, 4, 4, 383, 124, 124, 124, 124, + 384, 385, 385, 386, 387, 388, 389, 389, 390, 391, 392, 124, 124, 124, 393, 394, + 395, 396, 397, 398, 399, 400, 124, 124, 401, 401, 402, 403, 402, 404, 402, 402, + 405, 406, 407, 408, 409, 409, 410, 410, 411, 411, 124, 124, 412, 412, 413, 414, + 415, 415, 415, 416, 417, 418, 419, 420, 421, 422, 423, 124, 124, 124, 124, 124, + 424, 424, 424, 424, 425, 124, 124, 124, 426, 426, 426, 427, 426, 426, 426, 428, + 429, 429, 430, 431, 432, 432, 433, 432, 434, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 27, 435, 436, 436, 437, 438, 439, 440, 124, 441, + 442, 442, 443, 444, 444, 445, 124, 446, 447, 124, 124, 448, 449, 124, 450, 451, + 452, 452, 452, 452, 453, 454, 452, 455, 456, 456, 456, 456, 457, 458, 459, 460, + 461, 461, 461, 462, 463, 464, 464, 465, 466, 466, 466, 466, 466, 466, 467, 468, + 469, 470, 469, 469, 471, 124, 124, 124, 472, 473, 474, 475, 475, 475, 476, 477, + 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 487, 488, 489, 490, 491, 124, + 492, 492, 492, 492, 492, 493, 494, 124, 495, 495, 495, 495, 496, 497, 124, 124, + 498, 498, 498, 499, 498, 500, 124, 124, 501, 501, 501, 501, 502, 503, 504, 124, + 505, 505, 505, 506, 506, 137, 507, 124, 508, 509, 510, 508, 511, 124, 124, 124, + 512, 512, 512, 513, 124, 124, 124, 124, 124, 124, 514, 514, 514, 514, 514, 515, + 516, 517, 518, 519, 520, 521, 124, 124, 124, 124, 522, 523, 523, 522, 524, 124, + 525, 525, 525, 525, 526, 527, 527, 527, 527, 527, 528, 153, 529, 529, 529, 530, + 531, 124, 124, 124, 124, 124, 532, 124, 124, 124, 124, 124, 533, 533, 534, 535, + 536, 537, 537, 538, 539, 537, 540, 541, 541, 542, 543, 544, 124, 124, 124, 124, + 545, 546, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 555, 556, 557, 124, + 124, 124, 124, 124, 124, 124, 558, 559, 560, 561, 560, 562, 560, 563, 124, 124, + 124, 124, 124, 564, 565, 565, 565, 566, 567, 567, 567, 567, 567, 567, 567, 567, + 567, 568, 124, 124, 124, 124, 124, 124, 567, 567, 567, 567, 567, 567, 569, 570, + 567, 567, 567, 567, 571, 124, 124, 124, 124, 572, 572, 572, 572, 572, 572, 573, + 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 575, 574, 574, + 574, 574, 574, 574, 574, 574, 574, 576, 577, 577, 577, 577, 577, 577, 577, 577, + 577, 577, 577, 577, 578, 124, 124, 124, 579, 579, 579, 580, 124, 124, 124, 124, + 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 581, 582, 583, 584, 585, + 585, 585, 585, 586, 587, 588, 589, 590, 591, 591, 591, 591, 592, 593, 594, 595, + 591, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 596, 596, 596, 597, + 124, 124, 124, 124, 598, 598, 598, 598, 598, 599, 600, 601, 600, 602, 124, 124, + 603, 603, 603, 603, 604, 603, 603, 603, 605, 603, 124, 124, 124, 124, 606, 607, + 608, 608, 608, 608, 608, 608, 608, 608, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 610, 124, 611, 608, 612, 124, 124, 124, 124, 124, 124, + 608, 608, 608, 608, 608, 608, 608, 613, 124, 124, 124, 124, 124, 124, 124, 614, + 615, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, + 254, 254, 616, 617, 124, 618, 619, 620, 620, 620, 620, 620, 620, 620, 620, 620, + 620, 620, 620, 620, 620, 620, 620, 621, 622, 622, 622, 622, 622, 622, 623, 624, + 625, 626, 627, 124, 124, 124, 124, 124, 0, 0, 0, 0, 0, 0, 0, 340, + 0, 0, 0, 628, 0, 629, 0, 629, 8, 8, 194, 8, 630, 0, 0, 0, + 0, 0, 0, 0, 627, 124, 124, 124, 0, 0, 0, 0, 0, 0, 0, 631, + 0, 0, 632, 0, 0, 0, 633, 634, 635, 0, 636, 0, 0, 0, 234, 124, + 11, 11, 11, 11, 637, 124, 124, 124, 124, 124, 124, 124, 0, 627, 0, 627, + 0, 0, 0, 0, 0, 638, 0, 639, 0, 0, 0, 0, 0, 223, 0, 0, + 0, 640, 641, 642, 643, 0, 0, 0, 644, 645, 0, 646, 647, 648, 0, 0, + 0, 0, 649, 0, 0, 0, 0, 0, 0, 0, 0, 0, 650, 0, 0, 0, + 651, 651, 651, 651, 651, 651, 651, 651, 652, 653, 654, 124, 124, 124, 124, 124, + 4, 655, 656, 124, 124, 124, 124, 124, 657, 658, 659, 14, 14, 14, 660, 124, + 661, 124, 124, 124, 124, 124, 124, 124, 662, 662, 663, 664, 665, 124, 124, 124, + 124, 666, 667, 124, 668, 668, 668, 669, 124, 124, 124, 124, 124, 670, 670, 671, + 124, 124, 124, 124, 124, 672, 672, 673, 124, 124, 124, 124, 674, 675, 674, 676, + 124, 124, 124, 124, 124, 124, 677, 678, 679, 679, 679, 679, 679, 679, 679, 679, + 679, 679, 679, 679, 680, 681, 124, 124, 682, 682, 682, 682, 683, 684, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 324, 0, 0, 0, 685, 124, 124, 124, 124, + 324, 0, 0, 245, 124, 124, 124, 124, 686, 27, 687, 688, 689, 690, 691, 692, + 693, 694, 695, 694, 124, 124, 124, 696, 0, 0, 348, 0, 0, 0, 0, 0, + 0, 627, 225, 324, 324, 324, 0, 631, 0, 0, 245, 124, 124, 124, 697, 0, + 698, 0, 0, 348, 639, 227, 631, 124, 0, 0, 0, 0, 0, 699, 340, 340, + 0, 0, 0, 0, 0, 233, 348, 629, 348, 0, 0, 0, 700, 233, 0, 0, + 700, 0, 245, 348, 227, 639, 124, 124, 0, 0, 0, 0, 0, 700, 245, 340, + 701, 0, 0, 0, 702, 703, 704, 639, 0, 320, 0, 0, 0, 0, 0, 234, + 246, 246, 246, 246, 246, 246, 124, 124, 246, 312, 246, 246, 246, 246, 246, 246, + 246, 246, 312, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 705, 246, + 246, 246, 246, 246, 246, 312, 124, 124, 246, 312, 124, 124, 124, 124, 124, 124, + 246, 246, 246, 246, 706, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 313, + 707, 124, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 1, 2, 2, 2, + 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, + 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 8, 8, 8, 8, 16, 8, 8, 8, 17, 18, 18, 18, + 19, 19, 19, 19, 19, 20, 19, 19, 21, 22, 22, 22, 22, 22, 22, 22, + 22, 23, 21, 22, 22, 22, 23, 21, 24, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 12, 12, 25, 25, 26, 27, 25, 28, 12, 12, 29, 30, 29, 31, + 29, 29, 32, 32, 29, 29, 29, 29, 31, 29, 33, 7, 7, 34, 29, 29, + 35, 29, 29, 29, 29, 29, 29, 30, 36, 36, 36, 37, 36, 36, 36, 36, + 36, 36, 38, 39, 40, 40, 40, 40, 41, 12, 12, 12, 42, 42, 42, 42, + 42, 42, 43, 44, 45, 45, 45, 45, 45, 45, 45, 46, 45, 45, 45, 47, + 48, 48, 48, 48, 48, 48, 48, 49, 36, 36, 38, 12, 50, 51, 29, 29, + 52, 29, 29, 29, 53, 53, 53, 53, 54, 55, 53, 53, 53, 56, 53, 53, + 57, 58, 57, 59, 59, 57, 57, 57, 57, 57, 60, 57, 61, 62, 63, 57, + 57, 59, 59, 64, 12, 65, 12, 66, 57, 62, 57, 57, 57, 57, 57, 64, + 67, 67, 68, 69, 70, 71, 71, 71, 71, 71, 72, 71, 72, 73, 74, 72, + 68, 69, 70, 74, 75, 12, 67, 76, 12, 77, 71, 71, 71, 68, 12, 12, + 78, 78, 79, 80, 80, 79, 79, 79, 79, 79, 81, 79, 81, 78, 82, 79, + 79, 80, 80, 82, 83, 12, 12, 12, 79, 84, 79, 79, 82, 12, 78, 79, + 85, 85, 86, 87, 87, 86, 86, 86, 86, 86, 88, 86, 88, 85, 89, 86, + 86, 87, 87, 89, 12, 85, 12, 90, 86, 91, 86, 86, 86, 86, 12, 12, + 92, 93, 94, 92, 95, 96, 97, 95, 98, 99, 94, 92, 100, 100, 96, 92, + 94, 92, 95, 96, 99, 98, 12, 12, 12, 92, 100, 100, 100, 100, 94, 12, + 101, 101, 101, 102, 102, 101, 101, 101, 101, 101, 102, 101, 101, 101, 103, 101, + 101, 102, 102, 103, 12, 104, 105, 103, 101, 106, 101, 101, 12, 107, 101, 101, + 108, 108, 108, 109, 109, 108, 108, 108, 108, 108, 109, 108, 108, 110, 111, 108, + 108, 109, 109, 111, 12, 112, 12, 113, 108, 114, 108, 108, 110, 12, 12, 12, + 115, 115, 115, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 115, + 12, 115, 115, 115, 115, 117, 115, 115, 118, 118, 119, 119, 119, 120, 121, 119, + 119, 119, 119, 119, 122, 119, 119, 123, 119, 120, 124, 125, 119, 126, 119, 119, + 12, 121, 119, 119, 121, 127, 12, 12, 128, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 130, 131, 129, 129, 129, 12, 12, 12, 12, 12, 132, 133, 134, 135, + 135, 135, 135, 135, 135, 136, 135, 135, 135, 135, 135, 137, 135, 138, 135, 134, + 135, 135, 137, 135, 139, 139, 139, 139, 139, 139, 140, 139, 139, 139, 139, 141, + 140, 139, 139, 139, 139, 139, 139, 142, 139, 143, 144, 12, 145, 145, 145, 145, + 146, 146, 146, 146, 146, 147, 12, 148, 146, 146, 149, 146, 150, 150, 150, 150, + 151, 151, 151, 151, 151, 151, 152, 153, 151, 154, 152, 153, 152, 153, 151, 154, + 152, 153, 151, 151, 151, 154, 151, 151, 151, 151, 154, 155, 151, 151, 151, 156, + 151, 151, 153, 12, 157, 157, 157, 157, 157, 158, 157, 158, 159, 159, 159, 159, + 160, 160, 160, 160, 160, 160, 160, 161, 162, 162, 162, 162, 162, 162, 163, 164, + 162, 162, 165, 12, 166, 166, 166, 166, 166, 167, 12, 168, 169, 169, 169, 169, + 169, 170, 12, 12, 171, 171, 171, 171, 171, 12, 12, 12, 172, 172, 172, 173, + 173, 12, 12, 12, 174, 174, 174, 174, 174, 174, 174, 175, 174, 174, 175, 12, + 176, 177, 178, 178, 178, 178, 179, 12, 178, 178, 178, 178, 178, 178, 180, 12, + 178, 178, 181, 12, 159, 182, 12, 12, 183, 183, 183, 183, 183, 183, 183, 184, + 183, 183, 183, 12, 185, 183, 183, 183, 186, 186, 186, 186, 186, 186, 186, 187, + 186, 188, 12, 12, 189, 189, 189, 189, 189, 189, 189, 12, 189, 189, 190, 12, + 189, 189, 191, 192, 193, 193, 193, 193, 193, 193, 193, 194, 195, 195, 195, 195, + 195, 195, 195, 196, 195, 195, 195, 197, 195, 195, 198, 12, 195, 195, 195, 198, + 7, 7, 7, 199, 7, 7, 7, 12, 200, 200, 200, 200, 200, 200, 200, 201, + 202, 202, 202, 202, 203, 203, 203, 203, 203, 12, 12, 203, 204, 204, 204, 204, + 204, 204, 205, 204, 204, 204, 206, 207, 208, 208, 208, 208, 19, 19, 209, 12, + 146, 146, 210, 211, 202, 202, 12, 12, 212, 7, 7, 7, 213, 7, 214, 215, + 0, 214, 216, 12, 2, 217, 218, 2, 2, 2, 2, 219, 220, 217, 221, 2, + 2, 2, 222, 2, 2, 2, 2, 223, 8, 224, 8, 224, 8, 8, 225, 225, + 8, 8, 8, 224, 8, 15, 8, 8, 8, 10, 8, 226, 10, 15, 8, 14, + 0, 0, 0, 227, 0, 228, 0, 0, 229, 0, 0, 230, 0, 0, 0, 231, + 2, 2, 2, 232, 233, 12, 12, 12, 234, 12, 12, 12, 0, 235, 236, 0, + 4, 0, 0, 0, 0, 0, 0, 4, 2, 2, 5, 12, 0, 0, 233, 12, + 0, 0, 231, 12, 237, 237, 237, 237, 0, 238, 0, 0, 239, 239, 239, 239, + 18, 18, 18, 18, 18, 12, 240, 18, 241, 241, 241, 241, 241, 241, 12, 242, + 243, 12, 12, 242, 151, 154, 12, 12, 151, 154, 151, 154, 0, 0, 0, 233, + 244, 244, 244, 244, 244, 244, 245, 244, 244, 12, 12, 12, 244, 246, 12, 12, + 0, 247, 0, 0, 248, 244, 249, 250, 0, 0, 244, 0, 251, 252, 252, 252, + 252, 252, 252, 252, 252, 253, 254, 255, 256, 257, 257, 257, 257, 257, 257, 257, + 257, 257, 258, 256, 12, 259, 260, 260, 260, 260, 260, 260, 261, 150, 150, 150, + 150, 150, 150, 262, 0, 233, 12, 131, 150, 150, 150, 263, 257, 257, 257, 258, + 257, 257, 0, 0, 264, 264, 264, 264, 264, 264, 264, 265, 264, 266, 12, 12, + 267, 267, 267, 267, 268, 268, 268, 268, 268, 268, 268, 12, 269, 269, 269, 269, + 269, 269, 12, 12, 236, 2, 2, 2, 2, 2, 230, 2, 270, 2, 2, 2, + 271, 271, 271, 271, 271, 271, 271, 272, 273, 273, 273, 273, 273, 273, 12, 12, + 274, 274, 274, 274, 274, 275, 12, 276, 274, 274, 275, 12, 277, 277, 277, 277, + 277, 277, 277, 278, 279, 279, 279, 279, 279, 12, 12, 280, 150, 150, 150, 281, + 282, 282, 282, 282, 282, 282, 282, 283, 282, 282, 284, 285, 145, 145, 145, 286, + 287, 287, 287, 287, 287, 288, 12, 12, 287, 287, 287, 289, 287, 287, 289, 287, + 290, 290, 290, 290, 291, 12, 12, 12, 12, 12, 292, 290, 293, 293, 293, 293, + 293, 294, 12, 12, 155, 154, 155, 154, 155, 154, 12, 12, 2, 2, 3, 2, + 2, 295, 296, 12, 293, 293, 293, 297, 293, 293, 297, 12, 150, 12, 12, 12, + 150, 262, 298, 150, 150, 150, 150, 12, 244, 244, 244, 246, 244, 244, 246, 12, + 2, 299, 12, 12, 300, 22, 12, 24, 25, 26, 25, 301, 302, 303, 25, 25, + 29, 29, 29, 304, 7, 7, 7, 305, 231, 0, 0, 0, 0, 231, 0, 12, + 29, 306, 29, 29, 29, 29, 29, 307, 308, 0, 0, 0, 0, 309, 257, 257, + 257, 257, 257, 310, 311, 150, 311, 150, 311, 150, 311, 281, 0, 231, 0, 231, + 12, 12, 308, 233, 312, 312, 312, 313, 312, 312, 312, 312, 312, 314, 312, 312, + 312, 312, 314, 315, 312, 312, 312, 316, 312, 312, 314, 12, 231, 131, 0, 0, + 0, 131, 0, 0, 8, 8, 8, 14, 0, 0, 0, 317, 318, 12, 12, 12, + 0, 0, 0, 319, 320, 320, 320, 320, 320, 320, 320, 321, 322, 322, 322, 322, + 323, 12, 12, 12, 214, 0, 0, 0, 0, 0, 0, 12, 324, 324, 324, 324, + 324, 12, 12, 325, 326, 326, 326, 326, 326, 326, 327, 12, 328, 328, 328, 328, + 328, 328, 329, 12, 330, 330, 330, 330, 330, 330, 330, 331, 332, 332, 332, 332, + 332, 12, 332, 332, 332, 333, 12, 12, 334, 334, 334, 334, 335, 335, 335, 335, + 336, 336, 336, 336, 336, 336, 336, 337, 336, 336, 337, 12, 338, 338, 338, 338, + 338, 12, 338, 338, 338, 338, 338, 12, 339, 339, 339, 339, 339, 339, 12, 12, + 340, 340, 340, 340, 340, 12, 12, 341, 342, 342, 343, 342, 343, 344, 342, 342, + 344, 342, 342, 342, 344, 342, 344, 345, 346, 346, 346, 346, 346, 12, 12, 12, + 347, 347, 347, 347, 347, 348, 12, 12, 347, 349, 12, 12, 347, 347, 12, 12, + 2, 350, 2, 2, 351, 2, 299, 12, 352, 353, 354, 352, 352, 352, 352, 352, + 352, 355, 356, 357, 358, 358, 358, 358, 358, 359, 358, 358, 360, 360, 360, 360, + 361, 361, 361, 361, 361, 361, 361, 362, 12, 363, 361, 361, 364, 364, 364, 364, + 365, 366, 367, 364, 368, 368, 368, 368, 368, 368, 368, 369, 370, 370, 370, 370, + 370, 370, 371, 372, 373, 373, 373, 373, 373, 373, 374, 12, 375, 375, 375, 375, + 376, 376, 376, 376, 376, 376, 12, 376, 377, 376, 376, 376, 378, 379, 12, 378, + 378, 380, 380, 378, 378, 378, 378, 378, 378, 381, 382, 383, 378, 378, 384, 12, + 385, 385, 385, 385, 386, 386, 386, 386, 387, 387, 387, 387, 387, 388, 389, 387, + 387, 388, 12, 12, 390, 390, 390, 390, 390, 391, 392, 390, 393, 393, 393, 393, + 393, 394, 393, 393, 395, 395, 395, 395, 396, 12, 395, 395, 397, 397, 397, 397, + 398, 12, 399, 400, 12, 12, 399, 397, 401, 401, 401, 401, 401, 401, 402, 12, + 403, 403, 403, 403, 404, 12, 12, 12, 404, 12, 405, 403, 406, 406, 406, 406, + 406, 406, 12, 12, 406, 406, 407, 12, 408, 408, 408, 408, 408, 409, 410, 408, + 408, 409, 12, 411, 29, 29, 29, 412, 413, 413, 413, 413, 413, 413, 414, 415, + 415, 12, 12, 12, 416, 29, 12, 12, 29, 29, 417, 12, 12, 12, 416, 29, + 418, 418, 418, 418, 418, 418, 12, 12, 419, 419, 419, 419, 419, 419, 420, 12, + 421, 421, 421, 421, 421, 421, 422, 12, 423, 423, 423, 423, 423, 423, 423, 12, + 424, 424, 424, 424, 424, 425, 12, 12, 426, 426, 426, 426, 426, 426, 426, 427, + 428, 426, 426, 426, 426, 427, 12, 429, 430, 430, 430, 430, 431, 12, 12, 432, + 433, 433, 433, 433, 433, 433, 434, 12, 433, 433, 435, 12, 436, 436, 436, 436, + 436, 437, 436, 436, 436, 436, 12, 12, 438, 438, 438, 438, 438, 439, 12, 12, + 440, 440, 440, 440, 118, 119, 119, 119, 119, 127, 12, 12, 441, 441, 441, 441, + 442, 441, 441, 441, 443, 12, 12, 12, 444, 445, 446, 447, 444, 444, 444, 447, + 444, 444, 448, 12, 449, 449, 449, 449, 449, 449, 450, 12, 449, 449, 451, 12, + 452, 453, 452, 454, 454, 452, 452, 452, 452, 452, 455, 452, 455, 453, 456, 452, + 452, 454, 454, 457, 458, 459, 12, 453, 452, 460, 452, 458, 452, 458, 12, 12, + 461, 461, 462, 463, 461, 461, 461, 461, 461, 462, 461, 461, 464, 465, 466, 461, + 461, 462, 467, 12, 468, 12, 12, 12, 469, 469, 469, 469, 469, 469, 469, 470, + 471, 12, 12, 12, 472, 472, 472, 472, 472, 472, 12, 12, 472, 472, 473, 12, + 474, 474, 474, 474, 474, 475, 474, 474, 474, 474, 474, 475, 476, 476, 476, 476, + 476, 477, 12, 12, 476, 476, 478, 12, 178, 178, 178, 180, 479, 479, 479, 479, + 479, 479, 480, 12, 145, 12, 12, 12, 481, 481, 481, 481, 481, 481, 482, 483, + 481, 481, 481, 12, 481, 482, 12, 12, 484, 484, 484, 484, 484, 484, 484, 12, + 485, 485, 485, 485, 486, 12, 12, 487, 488, 489, 490, 488, 488, 491, 488, 488, + 488, 488, 488, 488, 488, 492, 493, 488, 488, 489, 12, 12, 488, 488, 494, 12, + 495, 495, 496, 495, 495, 495, 495, 495, 495, 497, 12, 12, 498, 498, 498, 498, + 498, 498, 12, 12, 499, 499, 499, 499, 500, 12, 12, 12, 501, 501, 501, 501, + 501, 501, 502, 12, 53, 53, 503, 12, 440, 440, 12, 12, 504, 504, 504, 504, + 505, 12, 12, 12, 504, 504, 505, 12, 506, 506, 507, 506, 506, 506, 506, 506, + 506, 508, 506, 506, 506, 509, 12, 12, 506, 506, 506, 510, 511, 511, 511, 511, + 512, 511, 511, 511, 511, 511, 513, 511, 511, 514, 12, 12, 515, 516, 517, 515, + 515, 515, 515, 515, 515, 516, 518, 517, 515, 515, 12, 12, 515, 515, 519, 12, + 520, 521, 522, 520, 520, 520, 520, 520, 520, 520, 520, 523, 521, 520, 524, 12, + 520, 520, 525, 12, 526, 526, 526, 526, 526, 526, 526, 12, 526, 526, 527, 12, + 528, 528, 528, 528, 528, 528, 529, 12, 530, 530, 530, 530, 531, 530, 530, 530, + 530, 530, 532, 533, 530, 530, 532, 12, 534, 12, 12, 12, 100, 100, 100, 100, + 96, 12, 12, 98, 535, 535, 535, 535, 535, 535, 536, 12, 535, 535, 535, 537, + 535, 538, 12, 12, 535, 12, 12, 12, 539, 539, 539, 539, 540, 12, 12, 12, + 541, 541, 541, 541, 541, 542, 12, 12, 541, 541, 543, 12, 544, 544, 544, 544, + 544, 545, 12, 12, 546, 546, 546, 546, 546, 546, 547, 12, 269, 269, 548, 12, + 549, 549, 549, 549, 549, 549, 549, 550, 549, 549, 551, 552, 553, 553, 553, 553, + 553, 553, 553, 554, 553, 553, 555, 12, 556, 556, 556, 556, 556, 556, 556, 557, + 556, 557, 12, 12, 558, 558, 558, 558, 558, 559, 12, 12, 558, 558, 560, 558, + 560, 558, 558, 558, 558, 558, 12, 561, 562, 562, 562, 562, 562, 562, 563, 12, + 564, 564, 564, 564, 564, 564, 565, 12, 566, 566, 566, 566, 566, 566, 567, 566, + 566, 12, 12, 12, 568, 568, 568, 568, 568, 568, 569, 570, 568, 568, 12, 570, + 571, 572, 12, 12, 244, 573, 12, 12, 574, 574, 574, 574, 575, 575, 575, 575, + 575, 576, 12, 12, 12, 12, 12, 577, 574, 574, 574, 578, 578, 12, 12, 12, + 257, 579, 257, 580, 581, 252, 252, 252, 582, 12, 12, 12, 583, 12, 12, 12, + 253, 584, 12, 12, 12, 257, 12, 12, 585, 585, 585, 585, 585, 585, 585, 12, + 586, 586, 586, 586, 586, 586, 587, 12, 586, 586, 586, 588, 586, 586, 588, 12, + 586, 586, 589, 586, 0, 12, 12, 12, 0, 12, 238, 0, 317, 12, 12, 12, + 7, 590, 12, 12, 0, 233, 12, 12, 0, 231, 308, 0, 0, 591, 227, 0, + 0, 0, 591, 7, 212, 592, 7, 0, 0, 0, 593, 227, 8, 224, 12, 12, + 0, 231, 12, 12, 0, 0, 317, 12, 0, 0, 0, 228, 594, 595, 308, 228, + 0, 0, 596, 308, 0, 308, 0, 0, 0, 596, 231, 308, 0, 228, 0, 228, + 0, 0, 596, 231, 0, 597, 238, 0, 228, 0, 0, 0, 0, 233, 0, 0, + 0, 0, 0, 238, 598, 598, 598, 598, 598, 598, 598, 12, 12, 12, 599, 598, + 600, 598, 598, 598, 2, 2, 2, 299, 12, 270, 299, 12, 239, 601, 239, 239, + 239, 239, 602, 239, 603, 604, 601, 12, 19, 19, 19, 605, 12, 12, 12, 606, + 607, 607, 607, 607, 607, 607, 607, 608, 607, 607, 607, 609, 607, 607, 609, 610, + 611, 611, 611, 611, 611, 611, 611, 612, 613, 613, 613, 613, 613, 613, 614, 615, + 616, 616, 616, 616, 616, 616, 617, 12, 618, 618, 618, 618, 618, 618, 619, 620, + 621, 621, 621, 621, 621, 621, 621, 622, 621, 623, 12, 624, 151, 154, 151, 625, + 151, 151, 151, 154, 626, 626, 626, 626, 626, 627, 626, 626, 626, 628, 12, 12, + 629, 629, 629, 629, 629, 629, 629, 12, 629, 629, 630, 631, 0, 317, 12, 12, + 29, 632, 29, 29, 633, 634, 632, 29, 412, 29, 635, 12, 636, 51, 635, 632, + 633, 634, 635, 635, 633, 634, 412, 29, 412, 29, 632, 637, 29, 29, 638, 29, + 29, 29, 29, 12, 632, 632, 638, 29, 50, 12, 12, 12, 12, 238, 0, 0, + 639, 12, 12, 12, 0, 0, 317, 0, 0, 0, 12, 12, 0, 0, 231, 238, + 0, 231, 317, 308, 0, 0, 0, 640, 0, 0, 231, 131, 641, 12, 12, 12, + 244, 244, 573, 12, 642, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, @@ -5831,8 +4348,7 @@ _hb_ucd_u16[5080] = 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, }; -static const int16_t -_hb_ucd_i16[92] = +static const int16_t _hb_ucd_i16[92]= { 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, @@ -5842,40 +4358,1271 @@ _hb_ucd_i16[92] = 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, }; -static inline uint_fast8_t -_hb_ucd_gc (unsigned u) +static inline uint8_t _hb_ucd_gc (unsigned u) { - return u<1114112u?_hb_ucd_u8[5208+(((_hb_ucd_u8[1168+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; + return u<1114110 ? _hb_ucd_u8[6560u+((_hb_ucd_u8[816u+((_hb_ucd_u16[((_hb_ucd_u8[272u+((_hb_ucd_u8[((((((((u)>>1))>>3))>>4))>>4)])<<4)+((((((((u)>>1))>>3))>>4))&15)])<<4)+((((((u)>>1))>>3))&15)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; } -static inline uint_fast8_t -_hb_ucd_ccc (unsigned u) +static inline uint8_t _hb_ucd_ccc (unsigned u) { - return u<125259u?_hb_ucd_u8[7206+(((_hb_ucd_u8[6638+(((_hb_ucd_u8[6162+(((_hb_ucd_u8[5802+(((_hb_ucd_u8[5556+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; + return u<125259 ? _hb_ucd_u8[8620u+((_hb_ucd_u8[8036u+((_hb_ucd_u8[7556u+((_hb_ucd_u8[7188u+((_hb_ucd_u8[6942u+((((((((u)>>2))>>2))>>2))>>3)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 0; } -static inline unsigned -_hb_ucd_b4 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline int_fast16_t -_hb_ucd_bmg (unsigned u) +static inline int16_t _hb_ucd_bmg (unsigned u) { - return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[8098+(((_hb_ucd_u8[7866+(((_hb_ucd_u8[7770+(((_hb_ucd_b4(7706+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; + return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[9516u+((_hb_ucd_u8[9284u+((_hb_ucd_u8[9188u+((_hb_ucd_b4(_hb_ucd_u8+9124u,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)])<<1)+((u)&1)] : 0; } -static inline uint_fast8_t -_hb_ucd_sc (unsigned u) +static inline uint8_t _hb_ucd_sc (unsigned u) { - return u<918016u?_hb_ucd_u8[11464+(((_hb_ucd_u8[10472+(((_hb_ucd_u8[9452+(((_hb_ucd_u8[8764+(((_hb_ucd_u8[8460+(((_hb_ucd_u8[8346+(u>>2>>2>>2>>3>>4)])<<4)+((u>>2>>2>>2>>3)&15u))])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; + return u<918000 ? _hb_ucd_u8[10950u+((_hb_ucd_u16[4648u+((_hb_ucd_u16[2608u+((_hb_ucd_u8[10214u+((_hb_ucd_u8[9764u+((((((((u)>>2))>>2))>>3))>>4)])<<4)+((((((((u)>>2))>>2))>>3))&15)])<<3)+((((((u)>>2))>>2))&7)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 2; } -static inline uint_fast16_t -_hb_ucd_dm (unsigned u) +static inline uint16_t _hb_ucd_dm (unsigned u) { - return u<195102u?_hb_ucd_u16[1656+(((_hb_ucd_u8[12834+(((_hb_ucd_u8[12452+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; + return u<195102 ? _hb_ucd_u16[7480u+((_hb_ucd_u8[13904u+((_hb_ucd_u8[13522u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; } + +#else + +#include + +static const uint8_t _hb_ucd_u8[13937]= +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 11, 12, 12, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 7, 21, 22, 22, 22, 23, 24, 7, 7, + 7, 25, 22, 22, 22, 26, 27, 28, 22, 29, 30, 31, 32, 33, 34, 35, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 22, 36, + 7, 7, 7, 7, 7, 7, 37, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 38, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 34, 34, 35, 36, 37, 34, 34, 34, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, + 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, + 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, + 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, + 34, 34,109,110,111,112,113,114,115,116,117,118, 34, 34, 34,119, + 120,121,122,123,124,125,126,127, 34,128,129,130,131,132,133,134, + 135,136,137,138,139,140,141,142,143,144,111,145,146,147,148,111, + 149,150,151,152,153,154,155,156,157,158,159,160,111,161,162,163, + 34, 34, 34, 34, 34, 34, 34, 34,164, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,165, + 34, 34, 34, 34, 34, 34, 34, 34,166, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,167,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34,168,169,170, 34,111,111,171,111,172,173,174,175, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,119, + 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34,176,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 67,177, 67, 67, 67,178,179, 67, + 67, 67,180,181,182,131, 65,111,183,184,185,186,187,188,189,190, + 67, 67, 67, 67,191,192,111,111,111,111,111,111,111,111,193,111, + 194,195,196,111,111,197,111,111,111,198,111,199,111,200,111, 34, + 34,201,202,111,111,111,111,111,131,203,204,111, 34,205,111,111, + 67, 67,206, 67, 67,111, 67,207, 67, 67, 67, 67, 67, 67, 67, 67, + 67,208, 67, 67, 67, 67, 67,177,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111, + 209,111,195,195,111,111,111,111,111,111,111,111,111,111,111,111, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, + 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, + 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 16, 43, 16, 10, + 40, 40, 40, 44, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 45, 34, 32, 34, 11, + 32, 46, 42, 42, 47, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 11, 11, 11, 11, 48, 2, 2, 2, 16, 16, 16, 16, 49, 50, 51, 52, + 53, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 54, + 55, 56, 42, 55, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 57, 2, 2, 2, 2, 2, 2, 58, 58, 58, 8, 9, 59, 2, 60, + 42, 42, 42, 42, 42, 56, 61, 2, 62, 36, 36, 36, 36, 63, 42, 42, + 7, 7, 7, 7, 7, 2, 2, 36, 64, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 65, 42, 42, 42, 66, 46, 42, 42, 67, 68, 69, 42, 42, 36, + 7, 7, 7, 7, 7, 36, 70, 71, 2, 2, 2, 2, 2, 2, 2, 72, + 63, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 64, 36, + 36, 36, 36, 42, 42, 42, 42, 42, 7, 7, 7, 7, 7, 36, 36, 36, + 36, 36, 36, 36, 36, 63, 42, 42, 42, 42, 39, 21, 2, 39, 68, 20, + 36, 36, 36, 42, 42, 68, 42, 42, 42, 42, 68, 42, 68, 42, 42, 42, + 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 63, 42, 42, 2, + 36, 36, 36, 36, 73, 36, 36, 36, 58, 58, 58, 74, 42, 42, 42, 42, + 36, 36, 36, 36, 75, 42, 42, 42, 42, 74, 42, 42, 42, 42, 42, 42, + 42, 76, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 64, 77, + 78, 42, 42, 42, 76, 77, 78, 77, 63, 42, 42, 42, 36, 36, 36, 36, + 36, 42, 2, 7, 7, 7, 7, 7, 79, 36, 36, 36, 36, 36, 36, 36, + 63, 77, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 77, + 78, 42, 42, 76, 77, 77, 78, 36, 36, 36, 36, 81, 77, 77, 36, 36, + 36, 42, 42, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 52, 57, 42, + 42, 76, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 77, + 78, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 64, 36, 36, 36, + 36, 36, 36, 7, 7, 7, 7, 7, 42, 36, 63, 2, 2, 2, 2, 2, + 78, 42, 42, 42, 76, 77, 78, 42, 59, 20, 20, 20, 82, 42, 42, 42, + 42, 77, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 78, + 78, 42, 42, 76, 77, 77, 78, 42, 42, 42, 42, 76, 77, 77, 36, 36, + 71, 27, 27, 27, 27, 27, 27, 27, 42, 64, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 77, 76, 77, 77, 77, 77, 77, 78, 42, + 36, 36, 36, 81, 77, 77, 77, 77, 77, 77, 77, 7, 7, 7, 7, 7, + 27, 83, 60, 60, 52, 60, 60, 60, 76, 77, 64, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 64, 42, 76, 77, 77, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 36, 36, 36, 36, 7, 7, 7, 84, 27, 27, 27, 83, + 63, 77, 65, 36, 36, 36, 36, 36, 77, 77, 77, 76, 77, 77, 42, 42, + 42, 42, 76, 77, 77, 77, 36, 36, 85, 81, 77, 77, 77, 77, 77, 77, + 42, 77, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 77, + 78, 42, 42, 77, 77, 77, 78, 70, 60, 60, 36, 81, 27, 27, 27, 86, + 27, 27, 27, 27, 83, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 76, + 77, 42, 42, 42, 77, 77, 77, 77, 7, 77, 2, 2, 2, 2, 2, 2, + 63, 36, 42, 42, 42, 42, 42, 87, 36, 36, 36, 68, 42, 42, 42, 56, + 7, 7, 7, 7, 7, 2, 2, 2, 63, 36, 42, 42, 42, 42, 64, 36, + 36, 36, 36, 39, 42, 42, 42, 42, 7, 7, 7, 7, 7, 7, 36, 36, + 70, 60, 2, 2, 2, 2, 2, 2, 2, 88, 88, 60, 42, 60, 60, 60, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 46, 46, 46, 4, 4, 77, + 63, 42, 42, 42, 42, 42, 42, 76, 42, 42, 56, 42, 36, 36, 63, 42, + 42, 42, 42, 42, 42, 42, 42, 60, 60, 60, 60, 69, 60, 60, 60, 60, + 2, 2, 88, 60, 21, 2, 2, 2, 36, 36, 36, 36, 36, 81, 78, 42, + 76, 42, 42, 42, 78, 76, 78, 64, 36, 36, 36, 77, 42, 36, 36, 42, + 64, 77, 80, 81, 77, 77, 77, 36, 63, 42, 64, 36, 36, 36, 36, 36, + 36, 76, 78, 76, 77, 77, 78, 81, 7, 7, 7, 7, 7, 77, 78, 60, + 16, 16, 16, 16, 16, 49, 43, 16, 36, 36, 36, 36, 36, 36, 63, 42, + 2, 2, 2, 2, 89, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 60, 60, 60, 60, 60, 60, 60, 60, 11, 11, 11, 11, 16, 16, 16, 16, + 90, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 65, + 91, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 92, 93, 93, + 36, 36, 36, 36, 36, 57, 2, 94, 95, 36, 36, 36, 36, 36, 36, 36, + 36, 42, 76, 77, 77, 77, 77, 80, 36, 42, 96, 2, 2, 2, 2, 2, + 36, 42, 42, 42, 42, 42, 42, 42, 36, 36, 42, 78, 42, 42, 42, 77, + 77, 77, 77, 76, 78, 42, 42, 42, 42, 42, 2, 79, 2, 59, 63, 42, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 97, 2, 55, 42, 74, + 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 36, 36, 36, 36, + 36, 36, 36, 36, 63, 36, 36, 36, 42, 76, 77, 78, 76, 77, 77, 77, + 77, 76, 77, 77, 78, 42, 42, 42, 60, 60, 2, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 27, 27, 60, 36, 36, 36, 63, 76, 78, 42, 2, + 36, 36, 81, 76, 42, 42, 42, 42, 76, 76, 78, 42, 42, 42, 76, 77, + 77, 78, 42, 42, 42, 42, 42, 42, 2, 2, 2, 79, 2, 2, 2, 2, + 42, 42, 42, 42, 42, 42, 42, 98, 42, 42, 80, 36, 36, 36, 36, 36, + 36, 36, 76, 42, 42, 76, 76, 77, 77, 76, 80, 36, 36, 36, 36, 2, + 88, 60, 60, 60, 60, 46, 42, 42, 42, 42, 60, 60, 60, 60, 21, 2, + 42, 80, 36, 36, 36, 36, 36, 36, 81, 42, 42, 77, 42, 78, 42, 36, + 36, 36, 36, 76, 42, 77, 78, 78, 42, 77, 77, 77, 77, 77, 2, 2, + 36, 36, 77, 77, 77, 77, 42, 42, 42, 42, 77, 42, 42, 56, 2, 2, + 7, 7, 7, 7, 7, 7, 85, 36, 36, 36, 36, 36, 39, 39, 39, 2, + 16, 16, 16, 16, 34, 16, 16, 16, 42, 56, 42, 42, 42, 42, 42, 42, + 76, 42, 42, 42, 64, 36, 63, 36, 36, 36, 64, 81, 42, 36, 36, 36, + 16, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 43, 16, 16, + 16, 16, 16, 16, 43, 16, 16, 16, 16, 16, 16, 16, 16, 99, 39, 39, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 16, 34, 11, 11, 11, 16, 16, 16, 16,100,100,100,100, + 16, 16, 16, 16, 11, 11,101,102, 40, 16, 16, 16, 11, 11,101, 40, + 16, 16, 16, 16, 11, 11,103, 40,104,104,104,104,104,105, 58, 58, + 50, 50, 50, 2,106,107,106,107, 2, 2, 2, 2,108, 58, 58,109, + 2, 2, 2, 2,110,111, 2,112,113, 2,114,115, 2, 2, 2, 2, + 2, 9,113, 2, 2, 2, 2,116, 58, 58, 58, 58, 58, 58, 58, 58, + 117, 39, 27, 27, 27, 8,114,118, 27, 27, 27, 27, 27, 8,114, 93, + 20, 20, 20, 20, 20, 20, 20, 20, 42, 42, 42, 42, 42, 42,119, 47, + 98, 47, 98, 42, 42, 42, 42, 42, 60,120, 60,121, 60, 34, 11, 16, + 11, 32,121, 60, 45, 11, 11, 60, 60, 60,120,120,120, 11, 11,122, + 11, 11, 35, 36,123, 60, 16, 11, 8, 8, 45, 16, 16, 26, 60,124, + 94, 94, 94, 94, 94, 94, 94, 94, 94,125,126, 94,127, 60, 60, 60, + 8, 8,128, 60, 60, 8, 60, 60,128, 26, 60,128, 60, 60, 60,128, + 60, 60, 60, 60, 60, 60, 60, 8, 60,128,128, 60, 60, 60, 60, 60, + 60, 60, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 60, 60, 60, 60, 4, 4, 60, 60, 8, 60, 60, 60,129,130, 60, 60, + 60, 60, 60, 60, 60, 60,128, 60, 60, 60, 60, 60, 60, 26, 8, 8, + 8, 8, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 8, 8, + 8, 60, 60, 60, 60, 60, 60, 60, 27, 27, 27, 27, 27, 27, 60, 60, + 60, 60, 60, 60, 60, 27, 27, 27, 60, 60, 60, 26, 60, 60, 60, 60, + 26, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 8, 8, 8, 8, + 60, 60, 60, 60, 60, 60, 60, 26, 60, 60, 60, 60, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 60, 60, 60, 60, 60, 60, + 8, 8,114,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,114,132,132,132,132,132,132,132,132,132,132,131, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,128, 26, 8, 8,128, 60, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 39, 11, 32, 32,124, 60, 60,121, 34,133, + 42, 32, 16, 16, 49, 2, 89, 2, 36, 36, 36, 36, 36, 36, 36, 75, + 2, 2, 2, 2, 2, 2, 2, 55, 2,106,106, 2,110,111,106, 2, + 2, 2, 2, 6, 2, 97,106, 2,106, 4, 4, 4, 4, 2, 2, 79, + 2, 2, 2, 2, 2, 50, 2, 2, 97,134, 2, 2, 2, 2, 2, 2, + 60, 2,135,132,132,132,136, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 1, 2,137,138, 4, 4, 4, 4, 4, 60, 4, 4, 4, 4,139, 93, + 140, 94, 94, 94, 94, 42, 42, 77,141, 39, 39, 60, 94,142, 57, 60, + 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63,143,144, 62, + 36, 36, 36, 36, 36, 57, 39, 62, 60, 27, 27, 60, 60, 60, 60, 60, + 27, 27, 27, 27, 27, 60, 60, 60, 60, 60, 60, 60, 27, 27, 27, 27, + 145, 27, 27, 27, 27, 27, 27, 27, 36, 36, 75, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36,146, 2, 32, 32, 32, 32, 32, 32, 32, 63, + 47,147, 42, 42, 42, 42, 42, 79, 32, 32, 32, 32, 32, 32, 39, 42, + 36, 36, 36, 94, 94, 94, 94, 94, 42, 2, 2, 2, 2, 2, 2, 2, + 40, 40, 40,144, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 43, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 41,148, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, + 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, + 48, 39,149, 35, 39, 35, 36, 36, 36, 64, 36, 64, 36, 63, 36, 36, + 36, 81, 78, 76, 60, 60, 42, 42, 27, 27, 27, 60,150, 60, 60, 60, + 36, 36, 2, 2, 2, 2, 2, 2, 77, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 77, 77, 77, 77, 77, 77, 77, 77, 42, 42, 42, 42, 42, 2, + 42, 36, 36, 36, 2, 65, 65, 63, 36, 36, 36, 42, 42, 42, 42, 2, + 36, 36, 36, 63, 42, 42, 42, 42, 42, 77, 77, 77, 77, 77, 77, 96, + 36, 63, 77, 42, 42, 77, 42, 77, 96, 2, 2, 2, 2, 2, 2, 79, + 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 63, 62, 36, 36, 36, 36, + 36, 36, 36, 36, 63, 42, 42, 76, 78, 76, 78, 42, 42, 42, 42, 42, + 36, 63, 36, 36, 36, 36, 76, 77, 7, 7, 7, 7, 7, 7, 2, 2, + 62, 36, 36, 70, 60, 81, 76, 36, 64, 42, 64, 63, 64, 36, 36, 42, + 36, 36, 36, 36, 36, 36, 75, 2, 36, 36, 36, 36, 36, 81, 42, 77, + 2, 75,151, 42, 42, 42, 42, 42, 16, 16, 16, 16, 16,102, 39, 39, + 16, 16, 16, 16, 99, 40, 40, 40, 36, 81, 78, 77, 76, 96, 78, 42, + 152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153, + 16, 16, 16, 16, 16, 16, 35, 64, 36, 36, 36, 36,154, 36, 36, 36, + 36, 40, 40, 40, 40, 40, 40, 40, 40, 22, 60, 60, 60, 60, 60, 60, + 60, 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,132, + 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 60, 60, 60, + 36, 36, 36, 36, 36, 36,150, 60, 2, 2, 2,135,115, 2, 2, 2, + 6,155,156,132,132,132,132,132,132,132,115,135,115, 2,112,157, + 2, 2, 2, 2,139,132,132,115, 2,158, 8, 8, 59, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36,159, 2, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,114,115, 4, 2, 36, 36, 36, 36, 36, + 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, + 20,160, 52, 20, 26, 8,128, 60, 60, 60, 60, 60,161, 58, 60, 60, + 2, 2, 2, 89, 27, 27, 27, 27, 27, 27, 27, 83, 60, 60, 60, 60, + 94, 94,127, 27, 83, 60, 60, 60, 60, 60, 60, 60, 60, 27, 60, 60, + 60, 60, 60, 60, 60, 60, 46, 42,162,162,162,162,162,162,162,162, + 163, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 86, 36, + 138, 36, 36, 36, 36, 94, 94, 94, 36, 36, 36, 36, 36, 36, 36, 57, + 164, 94, 94, 94, 94, 94, 94, 94, 11, 11, 11, 32, 16, 16, 16, 16, + 36, 36, 36, 57, 27, 27, 27, 27, 36, 36, 36, 70,145, 27, 27, 27, + 36, 36, 36,165, 27, 27, 27, 27, 36, 36, 36, 36, 36,165, 27, 27, + 36, 36, 36, 27, 27, 27, 27, 30, 36, 36, 36, 36, 36, 36, 27, 36, + 63, 42, 42, 42, 42, 42, 42, 42, 36, 36, 36, 36, 42, 42, 42, 42, + 36, 36, 36, 36, 36, 36,165, 30, 36, 36, 36, 36, 36, 36,165, 27, + 36, 36, 36, 36, 71, 36, 36, 36, 36, 36, 63, 42, 42,163, 27, 27, + 36, 36, 36, 36, 57, 2, 2, 2, 36, 36, 36, 36, 27, 27, 27, 27, + 16, 16, 16, 16, 16, 27, 27, 27, 36, 36, 42, 42, 42, 42, 42, 42, + 7, 7, 7, 7, 7, 36, 36, 62, 11, 11, 11, 11,166, 42, 42,141, + 16, 16, 16, 16, 16, 16, 16, 8, 36, 36, 36, 36, 36, 63,167, 50, + 88, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 42, 42, 42, + 27, 27, 27, 86, 36, 36, 36, 36,163, 27, 30, 2, 2, 2, 2, 2, + 36, 42, 42, 2, 2, 2, 2, 2, 36, 36,165, 27, 27, 27, 27, 27, + 78, 80, 36, 36, 36, 36, 36, 36, 42, 42, 42, 56, 2, 2, 2, 2, + 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 7, 7, 7, + 64, 63, 64, 36, 36, 36, 36, 63, 77, 78, 42, 76, 78, 56, 72, 2, + 2, 42, 42, 42, 42, 42, 66, 58, 36, 36, 36, 63, 42, 42, 78, 42, + 42, 42, 42, 7, 7, 7, 7, 7, 2, 2, 81, 80, 36, 36, 36, 36, + 36, 63, 2, 36, 36, 36, 36, 36, 36, 81, 77, 42, 42, 42, 42, 76, + 80, 36, 57, 2, 55, 42, 56, 78, 7, 7, 7, 7, 7, 57, 57, 2, + 89, 27, 27, 27, 27, 27, 27, 27, 36, 36, 36, 36, 36, 36, 77, 78, + 42, 77, 76, 42, 2, 2, 2, 64, 36, 36, 36, 36, 36, 36, 36, 63, + 76, 77, 77, 77, 77, 77, 77, 77, 36, 36, 36, 81, 77, 77, 80, 36, + 36, 77, 77, 42, 42, 42, 42, 42, 36, 36, 36, 36, 77, 78, 42, 42, + 42, 77, 77, 77, 77, 77, 77, 76, 64, 64, 2, 2, 2, 2, 2, 2, + 55, 42, 42, 42, 42, 42, 42, 42, 36, 36, 81, 77, 42, 42, 42, 42, + 77, 42, 76, 64, 36, 57, 2, 2, 7, 7, 7, 7, 7, 2, 2, 64, + 77, 78, 42, 42, 76, 76, 77, 78, 76, 42, 36, 65, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 81, 77, 42, 42, 42, 77, 77, 42, 78, + 56, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 42, + 77, 78, 42, 42, 42, 76, 78, 78, 56, 2, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 63, 78, 77, 42, 42, 42, 78, 57, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 63, 78, 77, 42, 42, 78, 42, 42, 42, 42, + 7, 7, 7, 7, 7, 27, 2, 88, 42, 42, 42, 42, 78, 56, 2, 2, + 27, 27, 27, 27, 27, 27, 27, 86, 77, 77, 77, 77, 77, 78, 76, 64, + 80, 78, 2, 2, 2, 2, 2, 2, 81, 77, 42, 42, 42, 42, 77, 77, + 64, 65, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 63, 42, 42, 42, 42, 64, 36, 36, 36, 63, 42, 42, 76, 63, 42, 56, + 2, 2, 2, 55, 42, 42, 42, 42, 63, 42, 42, 76, 78, 42, 36, 36, + 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 76, 42, 2, 65, 2, + 76, 42, 76, 76, 77, 77, 77, 77, 57, 2, 2, 2, 2, 2, 2, 2, + 42, 42, 42, 42, 42, 42, 42, 78, 2, 36, 36, 36, 36, 36, 36, 36, + 42, 42, 42, 42, 76, 42, 42, 42, 76, 42, 78, 42, 42, 42, 42, 42, + 42, 42, 42, 63, 42, 42, 42, 42, 36, 36, 36, 36, 36, 77, 77, 77, + 42, 76, 78, 78, 36, 36, 36, 36, 36, 36, 36, 36, 75, 36, 36, 36, + 36, 63, 76, 96, 2, 2, 2, 2, 42, 81, 36, 36, 36, 36, 36, 36, + 36, 36, 77, 42, 42, 42, 42, 77, 76, 56, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 42, 42, 42, 27, 27, 83, 60, 60, 60, 52, 20, + 150, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 21, + 64, 36, 36, 63, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 42, + 42, 42, 42, 42, 42, 77, 78, 42, 42, 42, 56, 2, 2, 2, 2, 2, + 42, 42, 42, 56, 2, 2, 60, 60, 39, 39, 88, 60, 60, 60, 60, 60, + 7, 7, 7, 7, 7,168, 27, 27, 27, 86, 36, 36, 36, 36, 36, 36, + 39, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 75,146, 2, + 27, 27, 27, 30, 2, 2, 2, 2, 11, 11, 11, 11, 11, 32, 16, 16, + 81, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 78, + 42, 67, 39, 39, 39, 39, 39, 39, 39, 79, 42, 42, 42, 42, 42, 42, + 77, 39, 94, 94, 94, 94, 94, 94, 36, 36, 36, 36, 36, 36, 46, 56, + 7, 7, 7, 7, 7, 60, 60, 60, 60, 60,169, 78, 42, 60,169, 77, + 77,170, 58, 58, 58, 74, 42, 42, 42, 69, 46, 42, 42, 42, 60, 60, + 60, 60, 60, 60, 60, 42, 42, 60, 60, 42, 69, 60, 60, 60, 60, 60, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 16, 7, + 42, 42, 42, 69, 60, 46, 42, 42, 42, 42, 42, 42, 42, 42, 69, 60, + 60, 60, 46, 60, 60, 60, 60, 60, 60, 60, 69, 21, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 55, 42, 42, 16, 16, 16, 16, 16,123, 16, 16, + 42, 42, 42, 67, 39, 39, 39, 39, 7, 7, 7, 7, 7, 7, 7, 70, + 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 7, 7,171, + 36, 36, 36, 36, 36, 75, 42, 42,172, 7, 7, 7, 7, 7, 7, 84, + 36, 63, 36, 64, 36, 36, 36, 42, 36, 36, 63, 42, 42, 42, 42, 75, + 16, 16, 42, 42, 42, 67, 39, 39, 27, 27, 27, 27, 27, 27,145, 27, + 173, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,145, + 27, 27, 27, 27, 27, 27, 83, 60, 60, 60, 60, 60, 60, 25, 40, 40, + 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, + 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, + 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, + 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, + 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, + 24, 24, 6, 24, 12, 12, 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, + 9, 6, 5, 21, 17, 17, 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, + 12, 21, 7, 21, 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, + 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, + 21, 1, 24, 7, 1, 12, 7, 6, 12, 10, 10, 10, 10, 12, 21, 6, + 10, 7, 7, 10, 23, 7, 15, 26, 13, 21, 13, 7, 15, 7, 12, 23, + 21, 26, 21, 15, 17, 7, 29, 7, 7, 22, 18, 18, 14, 14, 14, 7, + 10, 21, 17, 21, 11, 12, 5, 6, 8, 8, 8, 24, 5, 24, 9, 24, + 29, 29, 29, 1, 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, + 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, 15, 6, 18, 6, 12, 11, + 9, 26, 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, + 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 22, 21, 21, 22, + 18, 17, 26, 6, 7, 14, 17, 22, 26, 14, 17, 6, 14, 6, 12, 24, + 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, + 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 7, 1, + 25, 24, 26, 1, 2, 2, 12, 15, 21, 14, 7, 15, 9, 12, 12, 17, + 13, 15, 26, 10, 10, 1, 13, 23, 7, 13, 23, 15, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, + 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, + 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, + 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, + 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 43, 44, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, + 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, + 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, + 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, + 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, + 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, + 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, + 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, + 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, + 0, 0, 0, 0, 76, 77, 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, + 0, 83, 84, 0, 0, 85, 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, + 51, 91, 51, 0, 92, 0, 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, + 0, 96, 97, 98, 99, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0,100, + 101, 0, 0, 0, 0, 0, 0,102, 0, 0, 0, 0, 0, 0,103, 0, + 0, 0, 0, 0, 0,104,105, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,108, + 109, 0, 0,110, 0, 0, 0, 0, 0, 0,111, 0,112, 0,105, 0, + 0, 0, 0, 0,113,114, 0, 0, 0, 0, 0, 0, 0,115, 0, 0, + 0,116, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0,118, 0,119, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, + 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, + 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, + 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, 28, 29, + 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, 33, 0, + 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, + 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 43, + 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, + 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 0, + 57, 58, 0, 0, 0, 59, 60, 61, 62, 0, 0, 0, 0, 63, 52, 0, + 64, 65, 0, 0, 66, 0, 0, 0, 67, 68, 0, 0, 0, 69, 0, 70, + 71, 72, 73, 74, 1, 75, 0, 76, 77, 78, 0, 0, 79, 80, 0, 0, + 0, 81, 0, 0, 1, 1, 0, 0, 82, 0, 0, 83, 0, 0, 0, 0, + 79, 84, 0, 85, 0, 0, 0, 0, 0, 80, 86, 0, 87, 0, 52, 0, + 1, 80, 0, 0, 88, 0, 0, 89, 0, 0, 0, 0, 0, 90, 57, 0, + 0, 0, 0, 0, 0, 91, 92, 0, 0, 86, 0, 0, 33, 0, 0, 93, + 0, 0, 0, 0, 94, 0, 0, 0, 0, 49, 0, 0, 95, 0, 0, 0, + 0, 96, 97, 0, 0, 98, 0, 0, 99, 0, 0, 0,100, 0, 0, 0, + 101, 0, 0, 0,102, 0, 0, 0, 0,103,104, 95, 0, 0,105, 0, + 0, 0, 86, 0, 0,106, 0, 0, 0,107,108, 0, 0,109,110, 0, + 0, 0, 0, 0, 0,111, 0, 0,112, 0, 0, 0, 0,113, 33, 0, + 114,115,116, 57, 0, 0,117, 35, 0, 0,118, 0, 0, 0,119, 0, + 0, 0, 0, 0, 0,120, 0, 0,121, 0, 0, 0, 0,122, 90, 0, + 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,123, 0, 0, 0, 0,124, + 0, 0,125, 0, 0, 0, 0,123, 0, 0,126, 0, 0, 0, 0, 0, + 81, 0, 0, 0, 0,127, 0, 0, 0,128, 0, 0, 0,129, 0,130, + 0, 0, 0, 0,131,132,133, 0,134, 0,135, 0, 0, 0,136,137, + 138, 0, 79, 0, 0, 0, 0, 0, 35, 0, 0, 0,139, 0, 0, 0, + 140, 0, 0, 0,141, 0, 0, 0,142,143, 0,144, 0, 0,145, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, + 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, + 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, + 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, + 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, + 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, + 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, + 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, + 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, + 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, + 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, + 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, + 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, + 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, + 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, + 15, 86, 36, 10, 21, 1, 1, 1, 1, 41, 1, 21, 87, 0, 0, 55, + 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, 0, 89, + 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 90, 9, 12, 4, 91, 8, + 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, 94, 1, 1, 1, 1, 95, + 96, 97, 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, 0, 0, + 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0,102,103, 0, 0, + 104, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, 0, 0, 0, 0, + 0, 62, 0, 0,105, 68, 61, 0, 0, 0, 78, 0, 0, 0,106,107, + 58, 38, 81, 0, 0, 0, 0, 0, 0,108, 1, 14, 4, 12, 84, 0, + 0, 0, 0, 38, 90, 0, 0, 0, 0,109, 0, 0,110, 61, 0,111, + 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, 0, 0, + 112, 51, 0,112, 14, 52,113, 41, 0, 0, 62, 0, 0, 61, 0, 0, + 114, 0, 90, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0,114, + 0, 0, 0, 0,115, 0, 0, 0, 78, 55, 0, 38, 1, 58, 1, 58, + 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,116, 0, 0, 0, 55, 0, + 0, 0, 0,116, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, 0, 61, + 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, 8, 92, + 0, 0, 1, 90, 0, 0,117, 0, 0, 0, 0, 0, 0,118, 0,119, + 120,121,122, 0,105, 4,123, 49, 23, 0, 0, 0, 38, 50, 38, 58, + 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, 0, 0, + 0, 1, 0, 0, 0,124, 0, 0, 0,113, 19, 59, 0, 38, 0, 81, + 0, 0, 4,123, 0, 0, 0, 1,125, 0, 0, 0, 0, 0,230,230, + 230,230,230,232,220,220,220,220,232,216,220,220,220,220,220,202, + 202,220,220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220, + 220,220,220,230,230,230,230,240,230,220,220,220,230,230,230,220, + 220, 0,230,230,230,220,220,220,220,230,232,220,220,230,233,234, + 234,233,234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230, + 220,230,230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, + 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, + 30, 31, 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, + 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, + 0, 0, 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230, + 220,230,220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230, + 230,230,230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, + 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230, + 220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, + 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107, + 107,107,118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, + 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0, + 130,130,130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0, + 220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, + 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, + 0,220,230,220, 0,220,230,230,230,234, 0, 0, 9, 9, 0, 0, + 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234, + 214,220,202,230,230,230,230,230,232,228,228,220,218,230,233,220, + 230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, + 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, 0, 0, + 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, 0,220, + 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, 0, 0, + 230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, + 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226, + 216,216,216,216,216, 0,220,220,220, 0,232,232,220,230,230,230, + 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, 26, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, + 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, + 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, + 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, + 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, + 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, + 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, + 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, + 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, + 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, + 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, + 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, + 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, + 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, + 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, + 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, + 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, + 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, + 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, + 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, + 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, + 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, + 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, + 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, + 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, + 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, + 0, 0, 16, 50, 84,118,136,152,186,187,187,187,187,187,187,187, + 187,187,187,187,187,187,187,187,187,187,187,187,187,187, 12, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 13, 13, + 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 15, 16, 17, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 19, 32, 33, 33, 33, 33, 33, + 34, 19, 19, 19, 19, 19, 19, 35, 19, 36, 37, 38, 38, 38, 38, 38, + 38, 39, 40, 19, 19, 19, 19, 19, 19, 19, 41, 42, 19, 19, 43, 19, + 19, 19, 44, 45, 9, 46, 47, 48, 49, 50, 51, 52, 9, 9, 19, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 53, 19, 19, 53, 19, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 54, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 55, + 0, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 32, 33, 33, 33, 34, 35, 35, 35, 35, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 2, 2, 51, 51, 52, + 53, 54, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, + 57, 56, 56, 56, 56, 56, 56, 58, 59, 60, 61, 56, 62, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 56, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 71, 62, 62, 62, 62, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 73, 74, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 32, + 32, 32, 32, 32, 32, 32, 32, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 62, 62, 62, 62, 88, 89, 89, 89, 90, 89, 91, 92, 93, 94, 95, + 95, 96, 97, 87, 98, 99,100,101,102,103,104,105,105,105, 2,106, + 107,108,109,110,111,112,113,114,115,116,117, 89,118,119,120,121, + 122,123,124,125,126,127,128,129,130, 87,131,132,133,134, 87,135, + 136,137,138,139,140,141,142,143,144,145,146, 87,147,148,149,150, + 150,150,150,150,150,150,150,150,150,150, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87,151,152,152,152,152,152,152,152,152,153, + 153,153,153,153, 87, 87, 87, 87, 87,154, 87, 87, 87, 87, 87,155, + 155,155,155,156,157,158,158, 87, 87,159, 87,160,161,162,163,164, + 164,164,164,164,164,164,164,164,164,164,164,164,164,165,165,165, + 165,164,164, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,166,167, + 168,169,170,170,170, 87, 87,171,172, 87, 87, 87, 87, 87, 87, 56, + 56, 56, 56, 56, 56,173, 56, 56, 56,174,175, 51, 56, 56, 87,176, + 176,176,176,176,176, 87, 87, 87, 87, 87, 87, 87, 87, 2, 87,177, + 6,178, 87, 87,179, 87, 87, 87,180, 87,181, 87,182, 87, 33,183, + 183,184, 87, 87, 87, 87, 87, 56, 56, 56, 87, 89, 89, 87, 87, 56, + 56, 56, 56,185, 87, 56, 56, 62, 62, 62, 62, 62, 87, 87, 87, 62, + 87, 87, 87, 87, 87, 87, 87, 56, 87,186,186, 0, 1, 2, 2, 0, + 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, 8, + 8, 8, 8, 8, 8, 8, 9, 10, 11, 11, 11, 11, 11, 12, 11, 13, + 13, 13, 13, 13, 13, 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 15, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, 19, + 20, 21, 21, 22, 23, 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, 26, + 26, 26, 26, 26, 21, 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 26, 21, 21, 21, 21, 21, 21, 21, 31, 21, 32, + 32, 32, 32, 32, 33, 34, 32, 35, 35, 35, 35, 35, 35, 35, 35, 36, + 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 38, + 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, + 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 42, + 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 44, + 44, 44, 45, 44, 44, 44, 44, 46, 46, 46, 46, 46, 46, 46, 46, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 47, 47, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, + 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 56, + 56, 57, 57, 57, 57, 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, + 64, 64, 64, 64, 64, 64, 64, 65, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 55, 55, 55, 55, 55, 67, 67, 67, 67, 67, 68, 68, 68, 69, + 69, 69, 69, 69, 69, 64, 64, 70, 70, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 8, 8, 8, 8, 8, 72, 72, 72, 72, 72, 72, 72, 72, 73, + 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, + 50, 50, 50, 73, 77, 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, + 4, 4, 84, 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 85, + 0, 0, 0, 0, 0, 0, 86, 0, 4, 0, 0, 0, 8, 8, 8, 0, + 0, 87, 88, 89, 0, 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 90, 90, 90, 90, 90, 90, 90, 91, + 91, 91, 91, 91, 91, 4, 4, 92, 92, 92, 92, 92, 92, 92, 92, 50, + 50, 50, 93, 93, 93, 93, 93, 53, 53, 53, 53, 53, 53, 13, 13, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 0, 95, + 0, 96, 97, 98, 99, 99, 99, 99,100,101,102,102,102,102,103,104, + 104,104,105, 52, 52, 52, 52, 52, 0,104,104, 0, 0, 0,102, 52, + 52, 0, 0, 0, 0, 52,106, 0, 0, 0, 0, 0,102,102,107,102, + 102,102,102,102,108, 0, 0, 94, 94, 94, 94, 0, 0, 0, 0,109, + 109,109,109,109,109,109,109,109,109,109,109,109,110,110,110,111, + 111,111,111,111,111,111,111,111,111,111,111, 13, 13, 13, 13, 13, + 13,112,112,112,112,112,112, 0, 0,113, 4, 4, 4, 4, 4,114, + 4, 4, 4, 4, 4, 4, 4,115,115,115, 0,116,116,116,116,117, + 117,117,117,117,117, 32, 32,118,118,119,120,120,120, 52, 52,121, + 121,121,121,122,121, 49, 49,123,123,123,123,123,123, 49, 49,124, + 124,124,124,124,124,125,125, 53, 53, 53, 4, 4,126,127, 54, 54, + 54, 54, 54,125,125,125,125,128,128,128,128,128,128,128,128, 4, + 129, 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21,130, 21, 21, 21, 21, 8, 0,131, 0, 0, 0, 0, 21, 21, + 21, 21, 21, 21, 21, 21,132, 0, 0, 1, 2, 1, 2,133,101,102, + 134, 52, 52, 52, 52, 0, 0,135,135,135,135,135,135,135,135, 0, + 0, 0, 0, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 0,136,137, + 137,138,138,138,138,139, 0,140,140,140,141,141,142,142,142,143, + 143,144,144,144,144,144,144,145,145,145,145,145,146,146,146,147, + 147,147,148,148,148,148,148,149,149,149,150,150,150,150,151,151, + 151,151,151,152,152,152,152,153,153,153,153,153,153,153,153,154, + 154,154,154,155,155,156,156,157,157,157,157,157,157,158,158,159, + 159,160,160,161,161,161,161,162,162,163,163,163,163,163,163,164, + 164,164,164,164,164,165,165,166,166,166,166,167,167,167,167,168, + 168,168,168,169,169,170,170,171,171,171,171,171,171,171,171,172, + 172,172,172,172,172,172,172,173,173,173,173,173,173,173,173,174, + 174,174,174,175,175,175,175,175,175,175,175,175,175,175,175,176, + 176,176,176,177, 21, 21, 21,178,178,178,179,179,179,179,180,180, + 180,180,181,181,181,182,182,183,183,183,183,183,183,183,183,184, + 184,184,184,184,185,185,185,186,186,186,186,186,187,187,187,188, + 188,188,188,188,188,189, 43,190,190,190,190,190,190,190,190,191, + 191,191,192,192,192,192,192,193,193,193,194,193,193,193,193,195, + 195,195,195,195,195,195,195,196,196,196,196,196,196,196,196,197, + 197,197,197,197,197,197,197,198,198,198,198,198,198,198,198,199, + 199,199,199,199,199, 66, 66,200,200,200,200,200, 49, 49, 49,201, + 201,201,201,201,201,201,201,202,202,202,202,202,202,202,202,203, + 203,203,203,203,203,203,203,204,204,204,204,204,204,204,204,205, + 205,205,205,205,205,205,205,206,206,206,206,206,207,207,207,207, + 207,207, 55,208,208,208,208, 32, 32, 32, 32, 32, 32,188,188,209, + 209,209,209,209,209,209,209,210,210,210,210,210,210,210,211,211, + 211,211,211,211,211,211,211,212,212,212,212,212,212,213,213,213, + 213,213,214,214,214,214,214,215,215,215,215,215,215,215,215,216, + 216,216,216,216,216,216,216,110,110,110,110, 39, 39, 39, 39,217, + 217,217,217,217,217,217,217,218,218,218,218,218,218,218,218,219, + 219,219,219,219,219,219,219,220,220,220,220,220,220,220,220,221, + 221,221,221,221,221,221,221,112,112,112,112,112,112,112,112,112, + 112,112,112,222,222,222,223,223,223,223,223,223,224,224,224,225, + 225,225,225,225,225,225,225,226,226,226,226,226,226,226,226,227, + 227,227,227,227,227,227,227,227,227,228,228,228,228,228,228,229, + 229,229,229,229,229,229,229,229,229,229,229,229,229,230, 94,231, + 231,231,231,231,231,231,231,232,232,232,232,232,232,232,232,102, + 102,102,102,102,102,102,102,233, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99,102,234, 99,235,102,236,236, + 236,236,236,236,236,236,236,237,237,237,237,237,237,237,237,237, + 237, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 0,238,239,240, 0,241, 0, 0, 0, 0, 0,242, + 242,242,242,242,242,242,242, 91, 91, 91, 13, 13, 13, 13, 13,243, + 243,243,243,243,243,243,243,244,244,244,244,245,245,245,245,246, + 246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,248, + 248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,250, + 250,250,250,250,250,250,250,251, 0, 0, 0, 0, 0, 0, 0, 8, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 1, 2, 2, 2, 2, + 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, 2, + 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 8, 10, 8, + 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, 14, 14, 14, + 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, 19, 19, 19, + 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, 20, 20, 22, + 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, 20, 21, 27, + 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, + 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, 33, 33, 33, + 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, + 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, + 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, 48, + 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, 52, 52, 53, + 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, 56, 56, 57, + 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 60, + 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 0, 0, 66, + 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, 71, 71, 71, + 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, + 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, + 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, 7, 7, 83, + 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, 2, 89, 90, + 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, 0, 86, 1, + 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, 0, 4, 97, + 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, 99, 99,100, + 100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0,100, 0,105, + 106,106,106,106,106,106,106,106,106,107,105,108,109,109,109,109, + 109,109,109,109,109,110,108,111,111,111,111,112, 55, 55, 55, 55, + 55, 55,113,109,109,109,110,109,109, 0, 0,114,114,114,114,115, + 115,115,115,116,116,116,116,117,117,117,117, 96, 2, 2, 2, 2, + 2, 94, 2,118,118,118,118,119,119,119,119,120,120,120,120,121, + 121,121,121,121,121,121,122,123,123,123,123,124,124,124,124,124, + 124,124,125,126,126,126,126,127,127,127,127,128,128,128,128, 2, + 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, 17, 18, 20, + 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135,109,109,109, + 109,109,136,137,137,137,137, 0, 0, 0,138,139,139,139,139,140, + 140,140,140, 84, 0, 0, 0,141,141,141,141,142,142,142,142,143, + 143,143,143,144,144,144,144,145,145,145,145,146,146,146,146,147, + 147,147,147,148,148,148,148,149,149,149,149,150,150,150,150,151, + 151,151,151,152,152,152,152,153,153,153,153,154,154,154,154,155, + 155,155,155,156,156,156,156,157,157,157,157,158,158,158,158,159, + 159,159,159,160,160,160,160,161,161,161,161,162,162,162,162,163, + 163,163,163,164,164,164,164,165,165,165,165,166,166,166,166,167, + 167,167,167,168,168,168,168,169,169,169,169,170,170,170,170,171, + 171,171,171,172,172,172,172,173,173,173,173,174,174,174,174,175, + 175,175,175,176,176,176,176,177,177,177,177,178, 20, 20, 20,179, + 179,179,179,180,180,180,180,181,181,181,181,182,182,182,182,183, + 183,183,183,184,184,184,184,185,185,185,185,186,186,186,186,187, + 187,187,187,188,188,188,188,189,189,189,189,190, 45, 45, 45,191, + 191,191,191,192,192,192,192,193,193,193,193,194,194,194,194,194, + 194,195,194,196,196,196,196,197,197,197,197,198,198,198,198,199, + 199,199,199,200,200,200,200,201,201,201,201,202,202,202,202,203, + 203,203,203,204,204,204,204,205,205,205,205,206,206,206,206,207, + 207,207,207,208,208,208,208,209,209,209,209,210,210,210,210,211, + 211,211,211,212,212,212,212,213,213,213,213,214,214,214,214,215, + 215,215,215,216,216,216,216,217,217,217,217,218,218,218,218,219, + 219,219,219,220,220,220,220,221,221,221,221,222,222,222,222,223, + 223,223,223,224,224,224,224,225,225,225,225,226,226,226,226,227, + 227,227,227,228,228,228,228,229,229,229,229,230,230,230,230,231, + 232,232,232,233,233,233,233,232,232,232,232,234,106,106,106,235, + 106,106,106,106,236,109,109,237,237,237,237,238,238,238,238, 0, + 239, 86, 0, 0, 0,239, 7, 82,138, 7, 0, 0, 0,240, 86,241, + 241,241,241,242,242,242,242,243,243,243,243,244,244,244,244,245, + 245,245,245,246,246,246,246,247,247,247,247,248,248,248,248,249, + 249,249,249,250, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, + 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 9, + 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, 55, 55, 55, 55, 6, + 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, 4, 4, 4, 4, 4, + 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 0, 3, 3, 0, + 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, 3, 3, 1, + 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, 64, 64, 64, 64, 90, + 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, 7, 7, 7, 7, 7, + 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 11, + 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 22, 22, 22, 22, 23, + 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, 36, 36, 24, + 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, 25, 25, 25, 25, 25, + 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, 8, 8, 8, 8, 8, + 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 29, 29, 29, 29, 28, + 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 0, 0, + 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, 44, 0, 0, 0, 43, + 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, 32, 32, 0, 0, 32, + 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, 52, 52, 52, 52, 58, + 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, 62, 62, 62, 62, 76, + 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 73, 73, 73, 73, 1, + 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, + 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, 9, 9, 9, + 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, 0, + 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, 56, + 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, 0, 13, 0, 13, 0, + 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 0, 15, 15, 15, 15, + 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, 12, 0, 39, + 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, 60, + 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, 69, 69, 69, 69, 69, + 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, 84, 84, 84, 0, 68, + 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, 19, 9, 19, 19, 19, + 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, 3, 3, 0, 0, 1, + 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, 49, 49, 49, 49, 0, + 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, 42, 42, 42, 42, 41, + 41, 41, 41,118,118,118,118, 53, 53, 53, 53, 59, 59, 59, 59, 40, + 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50,135,135,135,135,106, + 106,106,106,104,104,104,104,161,161,161,161,170,170,170,170,110, + 110,110,110, 47, 47, 47, 47, 81, 81, 81, 81,120,120,120,120,116, + 116,116,116,128,128,128,128, 66, 66, 66, 66, 72, 72, 72, 72,173, + 173,173,173, 98, 98, 98, 98, 97, 97, 97, 97, 57, 57, 57, 57, 88, + 88, 88, 88,117,117,117,117,112,112,112,112, 78, 78, 78, 78, 83, + 83, 83, 83, 82, 82, 82, 82,122,122,122,122, 89, 89, 89, 89,130, + 130,130,130,144,144,144,144,165,165,165,165,156,156,156,156,156, + 156, 3, 3,147,147,147,147,148,148,148,148,158,158,158,158,153, + 153,153,153,149,149,149,149, 94, 94, 94, 94, 85, 85, 85, 85,101, + 101,101,101, 96, 96, 96, 96,111,111,111,111,100,100,100,100,100, + 36, 36, 36,108,108,108,108,129,129,129,129,109,109,109,109,107, + 107,107,107,107,107,107, 1,171,171,171,171,137,137,137,137,124, + 124,124,124,123,123,123,123,114,114,114,114,102,102,102,102,126, + 126,126,126,142,142,142,142,125,125,125,125,154,154,154,154,150, + 150,150,150,141,141,141,141,140,140,140,140,121,121,121,121,169, + 169,169,169,133,133,133,133,134,134,134,134,138,138,138,138,143, + 143,143,143,175,175,175,175,145,145,145,145,163,163,163,163, 63, + 63, 63, 63,157,157,157,157, 80, 80, 80, 80,127,127,127,127,166, + 166,166,166,115,115,115,115,159,159,159,159,103,103,103,103,119, + 119,119,119,167,167,167,167,146,146,146,146,172,172,172,172, 99, + 99, 99, 99,136,139, 13, 13,155,155,155,155,136,136,136,136, 17, + 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17,139,139,139,139,105, + 105,105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131,151, + 151,151,151,160,160,160,160,152,152,152,152,164,164,164,164,168, + 168,168,168,174,174,174,174,113,113,113,113,132,132,132,132, 15, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, + 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, + 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, 22, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 26, + 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, + 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, + 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, + 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, + 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, + 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, + 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101, + 102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, + 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, + 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0, + 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142, + 143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, + 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162, 0,163, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, 0, 0, 0, + 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0,170, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178,179, 0, 0, + 0,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194, + 195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210, + 211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, +}; +static const uint16_t _hb_ucd_u16[5104]= +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, + 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, + 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, + 47, 64, 65, 66, 47, 67, 47, 47, 68, 69, 47, 47, 70, 32, 71, 32, + 72, 47, 47, 73, 74, 75, 76, 77, 78, 47, 47, 79, 80, 81, 82, 83, + 84, 47, 47, 85, 86, 87, 88, 89, 84, 47, 47, 79, 90, 47, 82, 91, + 92, 47, 47, 93, 94, 95, 82, 96, 97, 47, 47, 98, 99, 100, 101, 102, + 103, 47, 47, 104, 105, 106, 82, 107, 108, 47, 47, 93, 109, 110, 82, 111, + 112, 47, 47, 113, 114, 115, 82, 116, 92, 47, 47, 47, 117, 118, 101, 119, + 47, 47, 47, 120, 121, 122, 66, 66, 47, 47, 47, 123, 124, 125, 47, 47, + 126, 127, 128, 129, 47, 47, 47, 130, 131, 32, 32, 132, 133, 134, 66, 66, + 47, 47, 135, 136, 122, 137, 138, 139, 140, 141, 9, 9, 9, 11, 11, 142, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 143, 144, 145, + 47, 146, 9, 9, 9, 9, 9, 147, 148, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 149, 47, 150, 151, 47, 47, 47, 47, 152, 153, + 47, 154, 47, 155, 47, 156, 47, 156, 47, 47, 47, 157, 158, 159, 160, 145, + 161, 160, 47, 47, 162, 47, 47, 47, 163, 47, 164, 47, 47, 47, 47, 47, + 47, 47, 165, 166, 167, 47, 47, 47, 47, 47, 47, 47, 47, 168, 146, 146, + 47, 169, 47, 47, 47, 170, 171, 172, 160, 160, 173, 174, 32, 32, 32, 32, + 175, 47, 47, 176, 177, 122, 178, 179, 180, 47, 181, 61, 47, 47, 182, 183, + 47, 47, 184, 185, 186, 61, 47, 187, 188, 9, 9, 9, 66, 189, 190, 191, + 11, 11, 192, 27, 27, 27, 193, 194, 11, 195, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 196, 13, 13, 13, 13, 13, 13, + 197, 197, 197, 197, 197, 198, 197, 11, 199, 199, 199, 200, 201, 202, 202, 201, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 27, 212, 212, 212, 213, 214, 32, + 215, 216, 217, 218, 219, 145, 220, 220, 221, 222, 223, 146, 224, 225, 146, 226, + 227, 227, 227, 227, 227, 227, 227, 227, 228, 146, 229, 146, 146, 146, 146, 230, + 146, 231, 227, 232, 146, 233, 234, 146, 146, 146, 146, 146, 146, 146, 145, 145, + 145, 235, 146, 146, 146, 146, 236, 145, 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 237, 238, 146, 146, 239, 146, 146, 146, 146, 146, 146, 240, 146, + 146, 146, 146, 146, 146, 146, 241, 242, 145, 243, 146, 146, 244, 227, 245, 227, + 246, 247, 227, 227, 227, 248, 227, 249, 146, 146, 146, 227, 250, 146, 146, 146, + 9, 9, 9, 11, 11, 11, 251, 252, 13, 13, 13, 13, 13, 13, 253, 254, + 11, 11, 11, 47, 47, 47, 255, 256, 47, 47, 47, 47, 47, 47, 32, 32, + 257, 258, 259, 260, 261, 262, 263, 263, 264, 265, 266, 267, 268, 47, 47, 47, + 47, 269, 148, 47, 47, 47, 47, 270, 47, 271, 47, 47, 146, 146, 146, 47, + 146, 146, 272, 146, 273, 274, 146, 146, 272, 146, 146, 274, 146, 146, 146, 146, + 47, 47, 47, 47, 146, 146, 146, 146, 47, 275, 47, 47, 47, 47, 47, 47, + 47, 146, 146, 146, 146, 47, 47, 187, 276, 47, 61, 47, 13, 13, 277, 278, + 13, 279, 47, 47, 47, 47, 280, 281, 31, 282, 283, 284, 13, 13, 13, 285, + 286, 287, 288, 289, 290, 291, 9, 292, 293, 47, 294, 295, 47, 47, 47, 296, + 297, 47, 47, 298, 299, 160, 32, 300, 61, 47, 301, 47, 302, 303, 47, 47, + 72, 47, 47, 304, 305, 306, 307, 61, 47, 47, 308, 309, 310, 311, 47, 312, + 47, 47, 47, 313, 58, 314, 315, 316, 47, 47, 47, 11, 11, 317, 318, 11, + 11, 11, 11, 11, 47, 47, 319, 160, 320, 320, 320, 320, 320, 320, 320, 320, + 321, 321, 321, 321, 321, 321, 321, 321, 11, 322, 323, 47, 47, 47, 47, 47, + 47, 47, 47, 324, 325, 326, 47, 47, 47, 47, 47, 327, 146, 47, 47, 47, + 47, 328, 47, 47, 329, 146, 146, 330, 32, 331, 32, 332, 333, 334, 335, 47, + 47, 47, 47, 47, 47, 47, 47, 336, 337, 2, 3, 4, 5, 338, 339, 340, + 47, 341, 47, 47, 47, 47, 342, 343, 344, 145, 145, 345, 220, 220, 220, 346, + 347, 146, 146, 146, 146, 146, 146, 348, 349, 349, 349, 349, 349, 349, 349, 349, + 47, 47, 47, 47, 47, 47, 350, 145, 47, 47, 351, 47, 352, 47, 47, 60, + 47, 353, 47, 47, 47, 354, 220, 220, 9, 9, 147, 11, 11, 47, 47, 47, + 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 353, 9, + 9, 355, 11, 11, 47, 47, 47, 47, 27, 27, 27, 27, 27, 27, 27, 27, + 47, 47, 47, 47, 47, 356, 47, 357, 47, 47, 358, 145, 145, 145, 47, 359, + 47, 360, 47, 353, 47, 47, 47, 47, 47, 47, 47, 361, 145, 145, 145, 145, + 362, 47, 47, 363, 145, 66, 47, 364, 47, 365, 145, 145, 366, 47, 367, 66, + 47, 47, 47, 368, 47, 369, 47, 369, 47, 368, 144, 145, 145, 145, 145, 145, + 9, 9, 9, 9, 11, 11, 11, 370, 47, 47, 371, 160, 372, 9, 373, 11, + 374, 227, 227, 227, 227, 227, 227, 227, 145, 145, 145, 145, 145, 145, 145, 145, + 47, 47, 375, 47, 275, 376, 146, 377, 47, 365, 378, 47, 60, 379, 66, 47, + 380, 66, 66, 47, 381, 145, 47, 47, 382, 47, 47, 363, 383, 384, 385, 386, + 180, 47, 47, 387, 388, 47, 47, 160, 97, 47, 389, 390, 391, 47, 47, 392, + 180, 47, 47, 393, 394, 395, 396, 145, 47, 47, 397, 398, 362, 32, 32, 32, + 47, 47, 368, 47, 47, 399, 172, 160, 92, 47, 47, 113, 400, 401, 402, 32, + 47, 47, 47, 403, 404, 405, 406, 32, 47, 47, 47, 407, 408, 409, 47, 47, + 47, 47, 47, 410, 411, 160, 160, 160, 47, 47, 412, 413, 414, 415, 32, 32, + 47, 47, 47, 416, 417, 160, 66, 66, 47, 47, 418, 419, 160, 160, 160, 160, + 47, 420, 421, 422, 47, 47, 47, 47, 47, 47, 397, 423, 66, 66, 66, 66, + 9, 9, 9, 9, 11, 11, 128, 424, 47, 47, 47, 425, 426, 160, 160, 160, + 47, 47, 47, 47, 47, 427, 428, 429, 430, 47, 47, 431, 432, 433, 47, 47, + 434, 435, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 436, 429, + 47, 47, 47, 47, 47, 47, 437, 160, 47, 47, 412, 438, 437, 128, 145, 439, + 47, 156, 440, 441, 32, 32, 32, 32, 47, 47, 47, 362, 442, 160, 47, 47, + 443, 444, 160, 47, 47, 445, 160, 160, 47, 47, 47, 47, 47, 47, 47, 446, + 447, 47, 47, 448, 449, 450, 32, 32, 47, 47, 47, 47, 145, 451, 452, 453, + 220, 220, 220, 220, 220, 220, 220, 66, 47, 47, 47, 47, 47, 47, 47, 437, + 47, 47, 47, 209, 454, 32, 47, 47, 47, 455, 456, 160, 160, 160, 160, 160, + 47, 47, 47, 47, 47, 47, 306, 47, 47, 47, 47, 47, 160, 47, 47, 457, + 47, 47, 47, 458, 459, 460, 461, 47, 27, 27, 27, 27, 462, 47, 463, 160, + 9, 9, 9, 9, 9, 9, 11, 11, 145, 464, 9, 465, 11, 11, 11, 11, + 47, 47, 47, 47, 399, 466, 429, 429, 467, 468, 27, 27, 27, 27, 469, 470, + 47, 471, 209, 209, 209, 209, 209, 209, 146, 146, 146, 146, 146, 146, 146, 472, + 146, 146, 146, 146, 146, 146, 146, 227, 32, 32, 32, 32, 32, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 473, 474, 475, 146, 476, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 477, 146, 146, 146, 9, 478, 11, 479, 480, 11, 197, 9, + 481, 482, 9, 483, 11, 9, 478, 11, 479, 480, 11, 197, 9, 481, 482, 9, + 483, 11, 9, 478, 11, 479, 480, 11, 197, 9, 481, 482, 9, 483, 11, 9, + 478, 11, 197, 9, 484, 485, 486, 487, 11, 488, 9, 489, 490, 491, 492, 11, + 493, 9, 494, 11, 495, 160, 160, 160, 32, 32, 32, 496, 32, 32, 497, 498, + 499, 500, 32, 32, 32, 32, 32, 32, 501, 11, 11, 11, 11, 11, 11, 11, + 32, 32, 32, 27, 27, 27, 27, 27, 32, 32, 32, 32, 32, 32, 32, 32, + 47, 47, 47, 502, 503, 146, 146, 146, 47, 47, 455, 32, 47, 47, 504, 505, + 47, 47, 47, 47, 47, 47, 506, 160, 47, 47, 47, 47, 47, 47, 455, 507, + 47, 47, 47, 47, 47, 47, 508, 509, 47, 47, 47, 47, 358, 32, 32, 32, + 9, 9, 481, 11, 510, 306, 66, 66, 145, 145, 511, 512, 145, 145, 145, 145, + 145, 145, 513, 145, 145, 145, 145, 145, 47, 47, 47, 47, 47, 47, 47, 227, + 514, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 515, + 146, 146, 146, 146, 146, 227, 227, 227, 209, 209, 209, 209, 209, 209, 209, 209, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, + 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, + 1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, + 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, + 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, + 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, + 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199, + 1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, + 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231, + 1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258, + 1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275, + 1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0, + 1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093, + 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 949,1134,1010, + 1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347, + 1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424, + 1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, + 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238, + 1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, + 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232, + 1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461, + 1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458,1468,1476,1480,1486, + 1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503,1494,1500,1508, 0, + 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0, + 1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, + 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, + 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560, + 1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, + 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, + 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, + 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, + 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0, + 1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, + 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, + 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, + 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, + 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, + 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0, + 1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, + 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, + 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356, + 1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214, + 1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363, + 1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251, + 1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266, + 1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287, + 1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302, + 1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, + 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375, + 1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353, + 1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234, + 1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403, + 1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412, + 1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0, + 1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721, + 1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0, + 1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757, + 1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776, + 1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0, + 1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793, + 1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814, + 1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0, + 1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728, + 1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764, + 1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821, + 1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, + 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828, + 1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833, + 1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, + 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, + 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, + 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, + 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938, + 1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0, + 1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870, + 1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0, + 1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0, + 1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0, + 1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, + 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0, + 1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0, + 1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0, + 1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, + 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, + 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, + 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, + 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, + 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, + 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, + 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, + 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, + 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, + 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, + 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, + 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, + 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, + 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, + 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, + 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, + 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, + 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, + 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, + 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, + 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, + 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, + 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, + 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, + 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, + 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, + 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, + 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, + 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, + 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, + 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575, + 1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0, + 1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0,1939, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1940, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1943,1944, 0, 0, 0, + 0, 0, 0,1945, 0,1946, 0, 0, 0, 0, 0, 0, 0, 0,1947, 0, + 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1950, 0,1949,1951, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1953,1952, 0,1954, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1955,1956, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1957, 0, 0, 0, 0, 0, 0, 0, + 0,1958,1961,1959,1965,1960,1962,1964,1963, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1967,1966,1968, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1969,1970, + 1971,1972,1973,1974,1975, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1976,1977,1978,1980,1979, + 1981, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, + 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, + 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, + 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, + 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, + 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, + 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, + 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, + 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, + 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, + 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, + 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, + 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, + 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, + 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, + 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, + 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, + 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, + 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, + 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, + 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, + 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, + 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, + 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, + 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, + 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, + 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, + 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, + 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, + 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, + 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, + 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, + 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, + 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t _hb_ucd_i16[92]= +{ + 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, + 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, + 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, + -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, + 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, +}; + +static inline uint8_t _hb_ucd_gc (unsigned u) +{ + return u<1114112 ? _hb_ucd_u8[5296u+((_hb_ucd_u8[1168u+((_hb_ucd_u16[((_hb_ucd_u8[544u+((_hb_ucd_u8[((((((((u)>>1))>>3))>>3))>>4)])<<4)+((((((((u)>>1))>>3))>>3))&15)])<<3)+((((((u)>>1))>>3))&7)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; +} +static inline uint8_t _hb_ucd_ccc (unsigned u) +{ + return u<125259 ? _hb_ucd_u8[7322u+((_hb_ucd_u8[6738u+((_hb_ucd_u8[6258u+((_hb_ucd_u8[5890u+((_hb_ucd_u8[5644u+((((((((u)>>2))>>2))>>2))>>3)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 0; +} +static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1)<<2))&15; +} +static inline int16_t _hb_ucd_bmg (unsigned u) +{ + return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[8218u+((_hb_ucd_u8[7986u+((_hb_ucd_u8[7890u+((_hb_ucd_b4(_hb_ucd_u8+7826u,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)])<<1)+((u)&1)] : 0; +} +static inline uint8_t _hb_ucd_sc (unsigned u) +{ + return u<918016 ? _hb_ucd_u8[11655u+((_hb_ucd_u8[10647u+((_hb_ucd_u8[9151u+((_hb_ucd_u8[8703u+((_hb_ucd_u8[8495u+((_hb_ucd_b4(_hb_ucd_u8+8466u,((((((((((u)>>2))>>2))>>3))>>3))>>4)))<<4)+((((((((((u)>>2))>>2))>>3))>>3))&15)])<<3)+((((((((u)>>2))>>2))>>3))&7)])<<3)+((((((u)>>2))>>2))&7)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 2; +} +static inline uint16_t _hb_ucd_dm (unsigned u) +{ + return u<195102 ? _hb_ucd_u16[1680u+((_hb_ucd_u8[13041u+((_hb_ucd_u8[12659u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; +} + + #endif - #endif /* HB_UCD_TABLE_HH */ /* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh index 4bc8d64c28f..711dd9b6571 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh @@ -7,13 +7,13 @@ * on file with this header: * * # emoji-data.txt - * # Date: 2024-05-01, 21:25:24 GMT - * # © 2024 Unicode®, Inc. + * # Date: 2025-07-25, 17:54:31 GMT + * # © 2025 Unicode®, Inc. * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. * # For terms of use and license, see https://www.unicode.org/terms_of_use.html * # * # Emoji Data for UTS #51 - * # Used with Emoji Version 16.0 and subsequent minor revisions (if any) + * # Version: 17.0 * # * # For documentation and usage, see https://www.unicode.org/reports/tr51 */ @@ -23,54 +23,62 @@ #include "hb-unicode.hh" -static const uint8_t -_hb_emoji_u8[464] = +#include + +static const uint8_t _hb_emoji_u8[624]= { 16, 17, 17, 17, 50, 20, 21, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,118,152, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 0, 3, 4, 0, 0, 5, 6, 0, 7, 0, 8, 9, 10, 11, 12, - 0, 0, 13, 0, 0, 0, 14, 0, 15, 0, 0, 0, 0, 16, 0, 0, - 17, 17, 18, 19, 20, 17, 17, 21, 17, 17, 22, 17, 23, 17, 24, 25, - 26, 27, 28, 17, 17, 17, 0, 0, 17, 17, 17, 17, 17, 17, 17, 29, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 4, 0, 0, - 5, 6, 0, 0, 7, 8, 0, 0, 8, 0, 9, 10, 0, 0, 11, 0, - 0, 12, 13, 14, 15, 16, 16, 16, 17, 16, 16, 16, 18, 19, 20, 21, - 22, 23, 0, 0, 0, 24, 0, 0, 25, 0, 26, 0, 0, 27, 0, 0, - 28, 0, 0, 0, 16, 16, 16, 16, 29, 9, 0, 30, 31, 32, 16, 33, - 34, 35, 36, 16, 16, 16, 16, 37, 16, 38, 39, 16, 16, 16, 40, 0, - 0, 0, 0, 41, 0, 0, 42, 16, 43, 0, 44, 0, 45, 46, 16, 16, - 47, 48, 49, 16, 16, 16, 16, 38, 0, 0, 0, 0, 0, 66, 0, 0, - 0, 0, 0, 16, 0, 2, 0, 0, 4, 0, 0, 2, 0, 0,240, 3, - 0, 6, 0, 0, 0, 0, 0, 12, 0, 1, 0, 0, 0,128, 0, 0, - 0,254, 15, 7, 4, 0, 0, 0, 0, 12, 64, 0, 1, 0, 0, 0, - 0, 0, 0,120,191,255,247,255,255,255,255,255, 63, 0,255,255, - 63,255, 87, 32, 2, 1, 24, 0,144, 80,184, 0,248, 0, 0, 0, - 0, 0,224, 0, 2, 0, 1,128, 0, 0, 48, 0,224, 0, 0, 24, - 0, 0, 33, 0, 0, 0, 1, 32, 0, 0,128, 2, 0,224, 0, 0, - 0,240, 3,192, 0, 64,254, 7, 0,224,255,255, 63, 0, 0, 0, - 254,255, 0, 4, 0,128,252,247, 0,254,255,255,255,255,255, 7, - 255,255,255, 63,192,255,255,255,255,255, 0, 0, 0, 0,240,255, - 0, 0,224,255, 0,240, 0, 0, 0,255, 0,252, 0,255, 0, 0, - 0,192,255,255, 0,240,255,255,255,255,255,247,191,255,255,255, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 3, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, + 0, 0, 0, 8, 0, 0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0, + 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 20, 0, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, + 23, 0, 24, 25, 0, 26, 27, 28, 29, 30, 31, 31, 32, 31, 33, 34, + 31, 31, 31, 35, 36, 37, 38, 39, 31, 40, 31, 41, 0, 0, 0, 42, + 43, 44, 45, 46, 47, 48, 31, 31, 0, 49, 31, 31, 0, 0, 0, 0, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 36, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 2, 0, 0,240, 3, 0, 6, 0, 0, + 0, 0, 0, 12, 0, 1, 0, 0, 0,128, 0, 0, 0,254, 15, 7, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 64, 0, + 1, 0, 0, 0, 0, 0, 0,120, 31, 64, 50, 33, 77,196, 0, 7, + 5,255, 15,128,105, 1, 0,200, 0, 0,252, 26,131, 12, 3, 96, + 48,193, 26, 0, 0, 6,191, 39, 36,191, 84, 32, 2, 1, 24, 0, + 144, 80,184, 0, 24, 0, 0, 0, 0, 0,224, 0, 2, 0, 1,128, + 0, 0, 0, 0, 0, 0, 48, 0,224, 0, 0, 24, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, + 0, 0,128, 2, 0, 0, 0, 0, 16, 0, 0, 0, 0,240, 0, 0, + 0, 0,240,255, 0,128, 1, 0, 1,128, 1, 0, 0, 0,192,255, + 0, 0, 0, 0, 0, 0, 3,192, 0, 64,254, 7, 0,192,255,255, + 255,255,255,255, 63, 0, 0, 0,254,255, 0, 4, 0,128,252,247, + 0,254,255,255,192,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,243,255,255,255,255,255,207,206,255,255,255,255, + 255,255,255,255,255,255,185, 7,255,255,255,255,255,255,255,191, + 255,255,255,255,255,255,255, 63, 0,126,255,255,255,128,249, 7, + 128, 60, 97, 0, 48, 1, 6, 16, 28, 0, 14,112, 10,129, 8,252, + 255,255, 0, 0, 0, 0, 0, 0, 63,248,231,255, 63,250,249,255, + 0, 0, 0,252,255,255,255,255, 0,240, 0, 0, 0, 0, 0, 0, + 0,255, 0,252, 0, 0, 0, 0, 0,255, 0, 0, 0,192, 0,240, + 252,255, 0,254,255,255,255,255, 0,240,255,255,255,255,255,247, + 191,255,255,255,255,255,255,255, 0, 0, 0,255, 0,192,255,255, }; -static inline unsigned -_hb_emoji_b4 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_emoji_b4 (const uint8_t* a, unsigned i) { - return (a[i>>1]>>((i&1u)<<2))&15u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline unsigned -_hb_emoji_b1 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_emoji_b1 (const uint8_t* a, unsigned i) { - return (a[i>>3]>>((i&7u)<<0))&1u; + return (a[i>>3]>>((i&7)<<0))&1; } -static inline uint_fast8_t -_hb_emoji_is_Extended_Pictographic (unsigned u) +static inline uint8_t _hb_emoji_is_Extended_Pictographic (unsigned u) { - return u<131070u?_hb_emoji_b1(264+_hb_emoji_u8,((_hb_emoji_u8[144+(((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>5>>2>>3))<<3)+((u>>5>>2)&7u))])<<2)+((u>>5)&3u))])<<5)+((u)&31u)):0; + return u<131070 ? _hb_emoji_b1(_hb_emoji_u8+224u,((_hb_emoji_u8[64u+((_hb_emoji_b4(_hb_emoji_u8,((((u)>>6))>>4)))<<4)+((((u)>>6))&15)])<<6)+((u)&63)) : 0; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh index 924105001d7..9e6f89365fe 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh @@ -241,6 +241,57 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE } } + static hb_codepoint_t + vertical_char_for (hb_codepoint_t u) + { + switch (u >> 8) + { + case 0x20: switch (u) { + case 0x2013u: return 0xfe32u; // EN DASH + case 0x2014u: return 0xfe31u; // EM DASH + case 0x2025u: return 0xfe30u; // TWO DOT LEADER + case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS + } break; + case 0x30: switch (u) { + case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA + case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP + case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET + case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET + case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET + case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET + case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET + case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET + case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET + case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET + case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET + case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET + case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET + case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET + case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET + case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET + } break; + case 0xfe: switch (u) { + case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE + } break; + case 0xff: switch (u) { + case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK + case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS + case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS + case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA + case 0xff1au: return 0xfe13u; // FULLWIDTH COLON + case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON + case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK + case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET + case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET + case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE + case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET + case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET + } break; + } + + return u; + } + struct { #define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name; HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-utf.hh b/src/java.desktop/share/native/libharfbuzz/hb-utf.hh index 7fd3bf8828b..3891fb331af 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-utf.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-utf.hh @@ -37,7 +37,7 @@ struct hb_utf8_t typedef uint8_t codepoint_t; static constexpr unsigned max_len = 4; - static const codepoint_t * + static inline const codepoint_t * next (const codepoint_t *text, const codepoint_t *end, hb_codepoint_t *unicode, @@ -106,7 +106,7 @@ struct hb_utf8_t return text; } - static const codepoint_t * + static inline const codepoint_t * prev (const codepoint_t *text, const codepoint_t *start, hb_codepoint_t *unicode, @@ -185,7 +185,7 @@ struct hb_utf16_xe_t typedef TCodepoint codepoint_t; static constexpr unsigned max_len = 2; - static const codepoint_t * + static inline const codepoint_t * next (const codepoint_t *text, const codepoint_t *end, hb_codepoint_t *unicode, @@ -217,7 +217,7 @@ struct hb_utf16_xe_t return text; } - static const codepoint_t * + static inline const codepoint_t * prev (const codepoint_t *text, const codepoint_t *start, hb_codepoint_t *unicode, @@ -294,7 +294,7 @@ struct hb_utf32_xe_t typedef TCodepoint codepoint_t; static constexpr unsigned max_len = 1; - static const TCodepoint * + static inline const TCodepoint * next (const TCodepoint *text, const TCodepoint *end HB_UNUSED, hb_codepoint_t *unicode, @@ -306,7 +306,7 @@ struct hb_utf32_xe_t return text; } - static const TCodepoint * + static inline const TCodepoint * prev (const TCodepoint *text, const TCodepoint *start HB_UNUSED, hb_codepoint_t *unicode, @@ -353,7 +353,7 @@ struct hb_latin1_t typedef uint8_t codepoint_t; static constexpr unsigned max_len = 1; - static const codepoint_t * + static inline const codepoint_t * next (const codepoint_t *text, const codepoint_t *end HB_UNUSED, hb_codepoint_t *unicode, @@ -363,7 +363,7 @@ struct hb_latin1_t return text; } - static const codepoint_t * + static inline const codepoint_t * prev (const codepoint_t *text, const codepoint_t *start HB_UNUSED, hb_codepoint_t *unicode, @@ -405,7 +405,7 @@ struct hb_ascii_t typedef uint8_t codepoint_t; static constexpr unsigned max_len = 1; - static const codepoint_t * + static inline const codepoint_t * next (const codepoint_t *text, const codepoint_t *end HB_UNUSED, hb_codepoint_t *unicode, @@ -417,7 +417,7 @@ struct hb_ascii_t return text; } - static const codepoint_t * + static inline const codepoint_t * prev (const codepoint_t *text, const codepoint_t *start HB_UNUSED, hb_codepoint_t *unicode, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh index cd27da96648..b650ca9f066 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh @@ -32,6 +32,12 @@ #include "hb-meta.hh" #include "hb-null.hh" +// Change to 1 to force inline vector allocs, to see callsite in malloc-stats tool. +#if 0 +#define HB_ALWAYS_INLINE_VECTOR_ALLOCS HB_ALWAYS_INLINE +#else +#define HB_ALWAYS_INLINE_VECTOR_ALLOCS +#endif template @@ -45,6 +51,7 @@ struct hb_vector_t using c_array_t = typename std::conditional, hb_array_t>::type; hb_vector_t () = default; + HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (std::initializer_list lst) : hb_vector_t () { alloc (lst.size (), true); @@ -57,18 +64,21 @@ struct hb_vector_t { extend (o); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (const hb_vector_t &o) : hb_vector_t () { alloc_exact (o.length); if (unlikely (in_error ())) return; copy_array (o.as_array ()); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (array_t o) : hb_vector_t () { alloc_exact (o.length); if (unlikely (in_error ())) return; copy_array (o); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (c_array_t o) : hb_vector_t () { alloc_exact (o.length); @@ -84,8 +94,29 @@ struct hb_vector_t } ~hb_vector_t () { fini (); } + template + void + set_storage (Type (&array)[n]) + { set_storage (array, n); } + void + set_storage (hb_array_t array) + { set_storage (array.arrayZ, array.length); } + template + void + set_storage (Type *array, unsigned n) + { + assert (allocated == 0); + assert (length == 0); + + arrayZ = array; + length = n; + } + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS void extend (const Iterable &o) { auto iter = hb_iter (o); @@ -106,12 +137,14 @@ struct hb_vector_t push_has_room (*iter++); } } + HB_ALWAYS_INLINE_VECTOR_ALLOCS void extend (array_t o) { alloc (length + o.length); if (unlikely (in_error ())) return; copy_array (o); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS void extend (c_array_t o) { alloc (length + o.length); @@ -136,10 +169,7 @@ struct hb_vector_t void fini () { - /* We allow a hack to make the vector point to a foreign array - * by the user. In that case length/arrayZ are non-zero but - * allocated is zero. Don't free anything. */ - if (allocated) + if (is_owned ()) { shrink_vector (0); hb_free (arrayZ); @@ -147,11 +177,13 @@ struct hb_vector_t init (); } - void reset () + HB_ALWAYS_INLINE_VECTOR_ALLOCS + hb_vector_t &reset () { if (unlikely (in_error ())) reset_error (); resize (0); + return *this; } friend void swap (hb_vector_t& a, hb_vector_t& b) noexcept @@ -237,13 +269,16 @@ struct hb_vector_t Type * operator + (unsigned int i) { return arrayZ + i; } const Type * operator + (unsigned int i) const { return arrayZ + i; } + HB_ALWAYS_INLINE_VECTOR_ALLOCS Type *push () { if (unlikely (!resize (length + 1))) return std::addressof (Crap (Type)); return std::addressof (arrayZ[length - 1]); } - template Type *push (Args&&... args) + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS + Type *push (Args&&... args) { if (unlikely ((int) length >= allocated && !alloc (length + 1))) // If push failed to allocate then don't copy v, since this may cause @@ -253,13 +288,20 @@ struct hb_vector_t return push_has_room (std::forward (args)...); } - template Type *push_has_room (Args&&... args) + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS + Type *push_has_room (Args&&... args) { /* Emplace. */ Type *p = std::addressof (arrayZ[length++]); return new (p) Type (std::forward (args)...); } + bool is_owned () const + { + return allocated != 0 && allocated != -1; + } + bool in_error () const { return allocated < 0; } void set_error () { @@ -271,27 +313,40 @@ struct hb_vector_t assert (allocated < 0); allocated = -(allocated + 1); } + void ensure_error () + { + if (!in_error ()) + set_error (); + } - template Type * - realloc_vector (unsigned new_allocated, hb_priority<0>) + _realloc (unsigned new_allocated) { if (!new_allocated) { - hb_free (arrayZ); + if (is_owned ()) + hb_free (arrayZ); return nullptr; } + if (!allocated && arrayZ) + { + /* If we have a non-null arrayZ but allocated is 0, then we are + * reallocating from a foreign array. */ + Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type)); + if (unlikely (!new_array)) + return nullptr; + hb_memcpy ((void *) new_array, (const void *) arrayZ, length * sizeof (Type)); + return new_array; + } return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type)); } - template Type * - realloc_vector (unsigned new_allocated, hb_priority<0>) + _malloc_move (unsigned new_allocated) { if (!new_allocated) { - hb_free (arrayZ); + if (is_owned ()) + hb_free (arrayZ); return nullptr; } Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type)); @@ -303,22 +358,33 @@ struct hb_vector_t new_array[i] = std::move (arrayZ[i]); arrayZ[i].~Type (); } - hb_free (arrayZ); + if (is_owned ()) + hb_free (arrayZ); } return new_array; } + + template + Type * + realloc_vector (unsigned new_allocated, hb_priority<0>) + { + return _realloc (new_allocated); + } + template + Type * + realloc_vector (unsigned new_allocated, hb_priority<0>) + { + return _malloc_move (new_allocated); + } /* Specialization for types that can be moved using realloc(). */ template Type * realloc_vector (unsigned new_allocated, hb_priority<1>) { - if (!new_allocated) - { - hb_free (arrayZ); - return nullptr; - } - return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type)); + return _realloc (new_allocated); } template other) { - assert ((int) (length + other.length) <= allocated); hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size); length += other.length; } @@ -362,7 +427,6 @@ struct hb_vector_t void copy_array (hb_array_t other) { - assert ((int) (length + other.length) <= allocated); hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size); length += other.length; } @@ -372,7 +436,6 @@ struct hb_vector_t void copy_array (hb_array_t other) { - assert ((int) (length + other.length) <= allocated); for (unsigned i = 0; i < other.length; i++) new (std::addressof (arrayZ[length + i])) Type (other.arrayZ[i]); length += other.length; @@ -385,7 +448,6 @@ struct hb_vector_t void copy_array (hb_array_t other) { - assert ((int) (length + other.length) <= allocated); for (unsigned i = 0; i < other.length; i++) { new (std::addressof (arrayZ[length + i])) Type (); @@ -398,12 +460,12 @@ struct hb_vector_t shrink_vector (unsigned size) { assert (size <= length); - if (!std::is_trivially_destructible::value) + if (!hb_is_trivially_destructible(Type)) { unsigned count = length - size; - Type *p = arrayZ + length - 1; + Type *p = arrayZ + length; while (count--) - p--->~Type (); + (--p)->~Type (); } length = size; } @@ -416,6 +478,7 @@ struct hb_vector_t } /* Allocate for size but don't adjust length. */ + HB_ALWAYS_INLINE_VECTOR_ALLOCS bool alloc (unsigned int size, bool exact=false) { if (unlikely (in_error ())) @@ -471,17 +534,64 @@ struct hb_vector_t return true; } + HB_ALWAYS_INLINE_VECTOR_ALLOCS bool alloc_exact (unsigned int size) { return alloc (size, true); } + HB_ALWAYS_INLINE_VECTOR_ALLOCS void clear () { resize (0); } - bool resize (int size_, bool initialize = true, bool exact = false) + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool allocate_from_pool (allocator_t *allocator, unsigned size, unsigned int initialize = true) + { + if (allocator) + { + assert (!length && !allocated); + arrayZ = (Type *) allocator->alloc (size * sizeof (Type), alignof (Type)); + if (unlikely (!arrayZ)) + { + set_error (); + return false; + } + if (initialize) + grow_vector (size, hb_prioritize); + else + length = size; + return true; + } + return resize_full ((int) size, initialize, true); + } + + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool duplicate_vector_from_pool (allocator_t *allocator, const hb_vector_t &other) + { + if (unlikely (!allocate_from_pool (allocator, other.length, false))) + return false; + length = 0; + copy_array (other.as_array ()); + return true; + } + + template + void shrink_back_to_pool (allocator_t *allocator, int size) + { + unsigned orig_length = length; + + shrink (size, false); + + if (allocator && !is_owned ()) + allocator->discard (arrayZ + length, (orig_length - length) * sizeof (Type)); + } + + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool resize_full (int size_, bool initialize, bool exact) { unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; if (!alloc (size, exact)) @@ -501,9 +611,20 @@ struct hb_vector_t length = size; return true; } - bool resize_exact (int size_, bool initialize = true) + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool resize (int size_) { - return resize (size_, initialize, true); + return resize_full (size_, true, false); + } + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool resize_dirty (int size_) + { + return resize_full (size_, false, false); + } + HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool resize_exact (int size_) + { + return resize_full (size_, true, true); } Type pop () @@ -544,7 +665,7 @@ struct hb_vector_t shrink_vector (size); - if (shrink_memory) + if (is_owned () && shrink_memory) alloc_exact (size); /* To force shrinking memory if needed. */ } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-version.h b/src/java.desktop/share/native/libharfbuzz/hb-version.h index e41286d2d8c..c673d1e3612 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-version.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-version.h @@ -41,26 +41,26 @@ HB_BEGIN_DECLS * * The major component of the library version available at compile-time. */ -#define HB_VERSION_MAJOR 11 +#define HB_VERSION_MAJOR 12 /** * HB_VERSION_MINOR: * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 2 +#define HB_VERSION_MINOR 3 /** * HB_VERSION_MICRO: * * The micro component of the library version available at compile-time. */ -#define HB_VERSION_MICRO 0 +#define HB_VERSION_MICRO 2 /** * HB_VERSION_STRING: * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "11.2.0" +#define HB_VERSION_STRING "12.3.2" /** * HB_VERSION_ATLEAST: diff --git a/src/java.desktop/share/native/libharfbuzz/hb.hh b/src/java.desktop/share/native/libharfbuzz/hb.hh index 8c430e5770d..7582abaa933 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb.hh @@ -89,7 +89,6 @@ #pragma GCC diagnostic error "-Wstring-conversion" #pragma GCC diagnostic error "-Wswitch-enum" #pragma GCC diagnostic error "-Wtautological-overlap-compare" -#pragma GCC diagnostic error "-Wuninitialized" #pragma GCC diagnostic error "-Wunneeded-internal-declaration" #pragma GCC diagnostic error "-Wunused" #pragma GCC diagnostic error "-Wunused-local-typedefs" @@ -110,11 +109,21 @@ #pragma GCC diagnostic warning "-Wformat-signedness" #pragma GCC diagnostic warning "-Wignored-pragma-optimize" #pragma GCC diagnostic warning "-Wlogical-op" -#pragma GCC diagnostic warning "-Wmaybe-uninitialized" #pragma GCC diagnostic warning "-Wmissing-format-attribute" +#pragma GCC diagnostic warning "-Wpessimizing-move" #pragma GCC diagnostic warning "-Wundef" #pragma GCC diagnostic warning "-Wunsafe-loop-optimizations" #pragma GCC diagnostic warning "-Wunused-but-set-variable" +#ifdef __clang__ +// The following are too buggy on gcc +// https://github.com/harfbuzz/harfbuzz/issues/5589 +// https://github.com/harfbuzz/harfbuzz/pull/5367 +#pragma GCC diagnostic warning "-Wmaybe-uninitialized" +#pragma GCC diagnostic warning "-Wuninitialized" +#else +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif #endif /* Ignored currently, but should be fixed at some point. */ @@ -136,6 +145,7 @@ #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic ignored "-Wformat-zero-length" #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic ignored "-Wold-style-cast" #pragma GCC diagnostic ignored "-Wpacked" // Erratic impl in clang #pragma GCC diagnostic ignored "-Wrange-loop-analysis" // https://github.com/harfbuzz/harfbuzz/issues/2834 #pragma GCC diagnostic ignored "-Wstrict-aliasing" @@ -239,6 +249,8 @@ // clang defines it so no need. #ifdef __has_builtin #define hb_has_builtin __has_builtin +#elif defined(_MSC_VER) +#define hb_has_builtin(x) 0 #else #define hb_has_builtin(x) ((defined(__GNUC__) && __GNUC__ >= 5)) #endif @@ -314,6 +326,10 @@ #endif #endif +#ifndef HB_HOT +#define HB_HOT __attribute__((hot)) +#endif + /* * Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411 * HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch @@ -553,4 +569,13 @@ extern "C" void hb_free_impl(void *ptr); #include "hb-vector.hh" // Requires: hb-array hb-null #include "hb-object.hh" // Requires: hb-atomic hb-mutex hb-vector + +/* Our src/test-*.cc use hb_assert(), such that it's not compiled out under NDEBUG. + * https://github.com/harfbuzz/harfbuzz/issues/5418 */ +#define hb_always_assert(x) \ + HB_STMT_START { \ + if (!(x)) { fprintf(stderr, "Assertion failed: %s, at %s:%d\n", #x, __FILE__, __LINE__); abort(); } \ + } HB_STMT_END + + #endif /* HB_HH */ From 67079b18afb4454fc849a35dd208ccf0b702339f Mon Sep 17 00:00:00 2001 From: David Holmes Date: Mon, 2 Feb 2026 22:29:15 +0000 Subject: [PATCH 092/215] 8377000: [BACKOUT] JDK-8376126 G1: Convert remaining volatiles in G1ConcurrentMark to Atomic Reviewed-by: kvn --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 70 ++++++++----------- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 32 ++++----- .../share/gc/g1/g1ConcurrentMark.inline.hpp | 14 ++-- .../share/gc/g1/g1RegionMarkStatsCache.hpp | 2 - 4 files changed, 54 insertions(+), 64 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 4ed0a3065bc..5f096c2b9d7 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -24,7 +24,6 @@ #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderDataGraph.hpp" -#include "cppstdlib/new.hpp" #include "gc/g1/g1BarrierSet.hpp" #include "gc/g1/g1BatchedTask.hpp" #include "gc/g1/g1CardSetMemory.hpp" @@ -520,8 +519,8 @@ G1ConcurrentMark::G1ConcurrentMark(G1CollectedHeap* g1h, _max_concurrent_workers(0), _region_mark_stats(NEW_C_HEAP_ARRAY(G1RegionMarkStats, _g1h->max_num_regions(), mtGC)), - _top_at_mark_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)), - _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(Atomic, _g1h->max_num_regions(), mtGC)), + _top_at_mark_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)), + _top_at_rebuild_starts(NEW_C_HEAP_ARRAY(HeapWord*, _g1h->max_num_regions(), mtGC)), _needs_remembered_set_rebuild(false) { assert(G1CGC_lock != nullptr, "CGC_lock must be initialized"); @@ -565,12 +564,6 @@ void G1ConcurrentMark::fully_initialize() { _tasks[i] = new G1CMTask(i, this, task_queue, _region_mark_stats); } - for (uint i = 0; i < _g1h->max_num_regions(); i++) { - ::new (&_region_mark_stats[i]) G1RegionMarkStats{}; - ::new (&_top_at_mark_starts[i]) Atomic{}; - ::new (&_top_at_rebuild_starts[i]) Atomic{}; - } - reset_at_marking_complete(); } @@ -583,7 +576,7 @@ PartialArrayStateManager* G1ConcurrentMark::partial_array_state_manager() const } void G1ConcurrentMark::reset() { - _has_aborted.store_relaxed(false); + _has_aborted = false; reset_marking_for_restart(); @@ -595,7 +588,7 @@ void G1ConcurrentMark::reset() { uint max_num_regions = _g1h->max_num_regions(); for (uint i = 0; i < max_num_regions; i++) { - _top_at_rebuild_starts[i].store_relaxed(nullptr); + _top_at_rebuild_starts[i] = nullptr; _region_mark_stats[i].clear(); } @@ -607,7 +600,7 @@ void G1ConcurrentMark::clear_statistics(G1HeapRegion* r) { for (uint j = 0; j < _max_num_tasks; ++j) { _tasks[j]->clear_mark_stats_cache(region_idx); } - _top_at_rebuild_starts[region_idx].store_relaxed(nullptr); + _top_at_rebuild_starts[region_idx] = nullptr; _region_mark_stats[region_idx].clear(); } @@ -643,7 +636,7 @@ void G1ConcurrentMark::reset_marking_for_restart() { } clear_has_overflown(); - _finger.store_relaxed(_heap.start()); + _finger = _heap.start(); for (uint i = 0; i < _max_num_tasks; ++i) { G1CMTaskQueue* queue = _task_queues->queue(i); @@ -665,14 +658,14 @@ void G1ConcurrentMark::set_concurrency(uint active_tasks) { void G1ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurrent) { set_concurrency(active_tasks); - _concurrent.store_relaxed(concurrent); + _concurrent = concurrent; if (!concurrent) { // At this point we should be in a STW phase, and completed marking. assert_at_safepoint_on_vm_thread(); assert(out_of_regions(), "only way to get here: _finger: " PTR_FORMAT ", _heap_end: " PTR_FORMAT, - p2i(finger()), p2i(_heap.end())); + p2i(_finger), p2i(_heap.end())); } } @@ -703,8 +696,8 @@ void G1ConcurrentMark::reset_at_marking_complete() { } G1ConcurrentMark::~G1ConcurrentMark() { - FREE_C_HEAP_ARRAY(Atomic, _top_at_mark_starts); - FREE_C_HEAP_ARRAY(Atomic, _top_at_rebuild_starts); + FREE_C_HEAP_ARRAY(HeapWord*, _top_at_mark_starts); + FREE_C_HEAP_ARRAY(HeapWord*, _top_at_rebuild_starts); FREE_C_HEAP_ARRAY(G1RegionMarkStats, _region_mark_stats); // The G1ConcurrentMark instance is never freed. ShouldNotReachHere(); @@ -1171,7 +1164,7 @@ void G1ConcurrentMark::concurrent_cycle_start() { } uint G1ConcurrentMark::completed_mark_cycles() const { - return _completed_mark_cycles.load_relaxed(); + return AtomicAccess::load(&_completed_mark_cycles); } void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) { @@ -1180,7 +1173,7 @@ void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) { _g1h->trace_heap_after_gc(_gc_tracer_cm); if (mark_cycle_completed) { - _completed_mark_cycles.add_then_fetch(1u, memory_order_relaxed); + AtomicAccess::inc(&_completed_mark_cycles, memory_order_relaxed); } if (has_aborted()) { @@ -1194,7 +1187,7 @@ void G1ConcurrentMark::concurrent_cycle_end(bool mark_cycle_completed) { } void G1ConcurrentMark::mark_from_roots() { - _restart_for_overflow.store_relaxed(false); + _restart_for_overflow = false; uint active_workers = calc_active_marking_workers(); @@ -1363,7 +1356,7 @@ void G1ConcurrentMark::remark() { } } else { // We overflowed. Restart concurrent marking. - _restart_for_overflow.store_relaxed(true); + _restart_for_overflow = true; verify_during_pause(G1HeapVerifier::G1VerifyRemark, VerifyLocation::RemarkOverflow); @@ -1792,45 +1785,44 @@ void G1ConcurrentMark::clear_bitmap_for_region(G1HeapRegion* hr) { } G1HeapRegion* G1ConcurrentMark::claim_region(uint worker_id) { - // "Checkpoint" the finger. - HeapWord* local_finger = finger(); + // "checkpoint" the finger + HeapWord* finger = _finger; - while (local_finger < _heap.end()) { - assert(_g1h->is_in_reserved(local_finger), "invariant"); + while (finger < _heap.end()) { + assert(_g1h->is_in_reserved(finger), "invariant"); - G1HeapRegion* curr_region = _g1h->heap_region_containing_or_null(local_finger); + G1HeapRegion* curr_region = _g1h->heap_region_containing_or_null(finger); // Make sure that the reads below do not float before loading curr_region. OrderAccess::loadload(); // Above heap_region_containing may return null as we always scan claim // until the end of the heap. In this case, just jump to the next region. - HeapWord* end = curr_region != nullptr ? curr_region->end() : local_finger + G1HeapRegion::GrainWords; + HeapWord* end = curr_region != nullptr ? curr_region->end() : finger + G1HeapRegion::GrainWords; // Is the gap between reading the finger and doing the CAS too long? - HeapWord* res = _finger.compare_exchange(local_finger, end); - if (res == local_finger && curr_region != nullptr) { - // We succeeded. + HeapWord* res = AtomicAccess::cmpxchg(&_finger, finger, end); + if (res == finger && curr_region != nullptr) { + // we succeeded HeapWord* bottom = curr_region->bottom(); HeapWord* limit = top_at_mark_start(curr_region); log_trace(gc, marking)("Claim region %u bottom " PTR_FORMAT " tams " PTR_FORMAT, curr_region->hrm_index(), p2i(curr_region->bottom()), p2i(top_at_mark_start(curr_region))); - // Notice that _finger == end cannot be guaranteed here since, - // someone else might have moved the finger even further. - assert(finger() >= end, "The finger should have moved forward"); + // notice that _finger == end cannot be guaranteed here since, + // someone else might have moved the finger even further + assert(_finger >= end, "the finger should have moved forward"); if (limit > bottom) { return curr_region; } else { assert(limit == bottom, - "The region limit should be at bottom"); + "the region limit should be at bottom"); // We return null and the caller should try calling // claim_region() again. return nullptr; } } else { - // Read the finger again. - HeapWord* next_finger = finger(); - assert(next_finger > local_finger, "The finger should have moved forward " PTR_FORMAT " " PTR_FORMAT, p2i(local_finger), p2i(next_finger)); - local_finger = next_finger; + assert(_finger > finger, "the finger should have moved forward"); + // read it again + finger = _finger; } } @@ -1970,7 +1962,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { void G1ConcurrentMark::abort_marking_threads() { assert(!_root_regions.scan_in_progress(), "still doing root region scan"); - _has_aborted.store_relaxed(true); + _has_aborted = true; _first_overflow_barrier_sync.abort(); _second_overflow_barrier_sync.abort(); } diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 39d98db9876..3a4cbf1b83e 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -368,7 +368,7 @@ class G1ConcurrentMark : public CHeapObj { // For grey objects G1CMMarkStack _global_mark_stack; // Grey objects behind global finger - Atomic _finger; // The global finger, region aligned, + HeapWord* volatile _finger; // The global finger, region aligned, // always pointing to the end of the // last claimed region @@ -395,19 +395,19 @@ class G1ConcurrentMark : public CHeapObj { WorkerThreadsBarrierSync _second_overflow_barrier_sync; // Number of completed mark cycles. - Atomic _completed_mark_cycles; + volatile uint _completed_mark_cycles; // This is set by any task, when an overflow on the global data // structures is detected - Atomic _has_overflown; + volatile bool _has_overflown; // True: marking is concurrent, false: we're in remark - Atomic _concurrent; + volatile bool _concurrent; // Set at the end of a Full GC so that marking aborts - Atomic _has_aborted; + volatile bool _has_aborted; // Used when remark aborts due to an overflow to indicate that // another concurrent marking phase should start - Atomic _restart_for_overflow; + volatile bool _restart_for_overflow; ConcurrentGCTimer* _gc_timer_cm; @@ -461,8 +461,8 @@ class G1ConcurrentMark : public CHeapObj { void print_and_reset_taskqueue_stats(); - HeapWord* finger() { return _finger.load_relaxed(); } - bool concurrent() { return _concurrent.load_relaxed(); } + HeapWord* finger() { return _finger; } + bool concurrent() { return _concurrent; } uint active_tasks() { return _num_active_tasks; } TaskTerminator* terminator() { return &_terminator; } @@ -487,7 +487,7 @@ class G1ConcurrentMark : public CHeapObj { // to satisfy an allocation without doing a GC. This is fine, because all // objects in those regions will be considered live anyway because of // SATB guarantees (i.e. their TAMS will be equal to bottom). - bool out_of_regions() { return finger() >= _heap.end(); } + bool out_of_regions() { return _finger >= _heap.end(); } // Returns the task with the given id G1CMTask* task(uint id) { @@ -499,10 +499,10 @@ class G1ConcurrentMark : public CHeapObj { // Access / manipulation of the overflow flag which is set to // indicate that the global stack has overflown - bool has_overflown() { return _has_overflown.load_relaxed(); } - void set_has_overflown() { _has_overflown.store_relaxed(true); } - void clear_has_overflown() { _has_overflown.store_relaxed(false); } - bool restart_for_overflow() { return _restart_for_overflow.load_relaxed(); } + bool has_overflown() { return _has_overflown; } + void set_has_overflown() { _has_overflown = true; } + void clear_has_overflown() { _has_overflown = false; } + bool restart_for_overflow() { return _restart_for_overflow; } // Methods to enter the two overflow sync barriers void enter_first_sync_barrier(uint worker_id); @@ -516,12 +516,12 @@ class G1ConcurrentMark : public CHeapObj { G1RegionMarkStats* _region_mark_stats; // Top pointer for each region at the start of marking. Must be valid for all committed // regions. - Atomic* _top_at_mark_starts; + HeapWord* volatile* _top_at_mark_starts; // Top pointer for each region at the start of the rebuild remembered set process // for regions which remembered sets need to be rebuilt. A null for a given region // means that this region does not be scanned during the rebuilding remembered // set phase at all. - Atomic* _top_at_rebuild_starts; + HeapWord* volatile* _top_at_rebuild_starts; // True when Remark pause selected regions for rebuilding. bool _needs_remembered_set_rebuild; public: @@ -679,7 +679,7 @@ public: uint completed_mark_cycles() const; - bool has_aborted() { return _has_aborted.load_relaxed(); } + bool has_aborted() { return _has_aborted; } void print_summary_info(); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp index 21167d5cae9..2f4824e4cae 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.inline.hpp @@ -194,11 +194,11 @@ inline void G1CMTask::process_array_chunk(objArrayOop obj, size_t start, size_t inline void G1ConcurrentMark::update_top_at_mark_start(G1HeapRegion* r) { uint const region = r->hrm_index(); assert(region < _g1h->max_num_regions(), "Tried to access TAMS for region %u out of bounds", region); - _top_at_mark_starts[region].store_relaxed(r->top()); + _top_at_mark_starts[region] = r->top(); } inline void G1ConcurrentMark::reset_top_at_mark_start(G1HeapRegion* r) { - _top_at_mark_starts[r->hrm_index()].store_relaxed(r->bottom()); + _top_at_mark_starts[r->hrm_index()] = r->bottom(); } inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) const { @@ -207,7 +207,7 @@ inline HeapWord* G1ConcurrentMark::top_at_mark_start(const G1HeapRegion* r) cons inline HeapWord* G1ConcurrentMark::top_at_mark_start(uint region) const { assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region); - return _top_at_mark_starts[region].load_relaxed(); + return _top_at_mark_starts[region]; } inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const { @@ -217,7 +217,7 @@ inline bool G1ConcurrentMark::obj_allocated_since_mark_start(oop obj) const { } inline HeapWord* G1ConcurrentMark::top_at_rebuild_start(G1HeapRegion* r) const { - return _top_at_rebuild_starts[r->hrm_index()].load_relaxed(); + return _top_at_rebuild_starts[r->hrm_index()]; } inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) { @@ -225,10 +225,10 @@ inline void G1ConcurrentMark::update_top_at_rebuild_start(G1HeapRegion* r) { uint const region = r->hrm_index(); assert(region < _g1h->max_num_regions(), "Tried to access TARS for region %u out of bounds", region); - assert(top_at_rebuild_start(r) == nullptr, + assert(_top_at_rebuild_starts[region] == nullptr, "TARS for region %u has already been set to " PTR_FORMAT " should be null", - region, p2i(top_at_rebuild_start(r))); - _top_at_rebuild_starts[region].store_relaxed(r->top()); + region, p2i(_top_at_rebuild_starts[region])); + _top_at_rebuild_starts[region] = r->top(); } inline void G1CMTask::update_liveness(oop const obj, const size_t obj_size) { diff --git a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp index b8f13f4553d..4dcdd33846e 100644 --- a/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp +++ b/src/hotspot/share/gc/g1/g1RegionMarkStatsCache.hpp @@ -44,8 +44,6 @@ struct G1RegionMarkStats { Atomic _live_words; Atomic _incoming_refs; - G1RegionMarkStats() : _live_words(0), _incoming_refs(0) { } - // Clear all members. void clear() { _live_words.store_relaxed(0); From 1cb4ef8581b5c5572474a5376baf4fd88c5ffeab Mon Sep 17 00:00:00 2001 From: David Holmes Date: Mon, 2 Feb 2026 22:39:31 +0000 Subject: [PATCH 093/215] 8376855: ASAN reports out-of-range read in strncmp in MethodHandles::is_basic_type_signature Reviewed-by: azafari, jsjolen --- src/hotspot/share/prims/methodHandles.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index c243cae20ab..584f077eddc 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -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 @@ -557,7 +557,7 @@ bool MethodHandles::is_basic_type_signature(Symbol* sig) { switch (ss.type()) { case T_OBJECT: // only java/lang/Object is valid here - if (strncmp((char*) ss.raw_bytes(), OBJ_SIG, OBJ_SIG_LEN) != 0) + if (strncmp((char*) ss.raw_bytes(), OBJ_SIG, ss.raw_length()) != 0) return false; break; case T_VOID: From caf1338243004e62c8a9e5fc8ba5d5e19f6edba2 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Tue, 3 Feb 2026 02:21:06 +0000 Subject: [PATCH 094/215] 8376700: java/nio/file/DirectoryStream/SecureDS.java fails AtomicMoveNotSupportedException Reviewed-by: bpb --- .../nio/file/DirectoryStream/SecureDS.java | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index 21204ba980a..a115d56c52f 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -21,31 +21,43 @@ * questions. */ -/* @test +/* @test id=tmp * @bug 4313887 6838333 8343020 8357425 * @summary Unit test for java.nio.file.SecureDirectoryStream * @requires (os.family == "linux" | os.family == "mac" | os.family == "aix") * @library .. /test/lib - * @build jdk.test.lib.Platform + * @build jdk.test.lib.Platform jtreg.SkippedException * @run main SecureDS */ +/* @test id=cwd + * @requires (os.family == "linux" | os.family == "mac" | os.family == "aix") + * @library .. /test/lib + * @build jdk.test.lib.Platform jtreg.SkippedException + * @run main SecureDS cwd + */ + import java.nio.file.*; import static java.nio.file.Files.*; import static java.nio.file.StandardOpenOption.*; import static java.nio.file.LinkOption.*; import java.nio.file.attribute.*; -import java.nio.channels.*; import java.io.IOException; import java.util.*; import jdk.test.lib.Platform; +import jtreg.SkippedException; public class SecureDS { static boolean supportsSymbolicLinks; public static void main(String[] args) throws IOException { - Path dir = TestUtil.createTemporaryDirectory(); + Path dir; + if (args.length > 0 && args[0].equals("cwd")) { + dir = TestUtil.createTemporaryDirectory(System.getProperty("user.dir")); + } else { + dir = TestUtil.createTemporaryDirectory(); + } try { DirectoryStream stream = newDirectoryStream(dir); stream.close(); @@ -301,7 +313,17 @@ public class SecureDS { Files.writeString(filepath, TEXT); try (DirectoryStream ds = Files.newDirectoryStream(dir);) { if (ds instanceof SecureDirectoryStream sds) { - sds.move(file, null, file); + try { + sds.move(file, null, file); + } catch (AtomicMoveNotSupportedException e) { + if (Files.getFileStore(cwd).equals(Files.getFileStore(dir))) { + // re-throw if move between same volume + throw e; + } else { + throw new SkippedException( + "java.nio.file.AtomicMoveNotSupportedException"); + } + } if (!TEXT.equals(Files.readString(result))) throw new RuntimeException(result + " content incorrect"); } else { @@ -311,11 +333,10 @@ public class SecureDS { boolean fileDeleted = Files.deleteIfExists(filepath); if (!fileDeleted) Files.deleteIfExists(result); + // clean-up + delete(dir1); + delete(dir2); } - - // clean-up - delete(dir1); - delete(dir2); } // null and ClosedDirectoryStreamException From e21cb8525d91e91f000dc375b250c4acd37314e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20H=C3=BCbner?= Date: Tue, 3 Feb 2026 06:32:50 +0000 Subject: [PATCH 095/215] 8370441: Remove unnecessary/confusing null check in Verifier::verify() Reviewed-by: dholmes, coleenp --- src/hotspot/share/classfile/verifier.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 38dba1d3d5f..30f147b9ae7 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -190,9 +190,8 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { // effect (sic!) for external_name(), but instead of doing that, we opt to // explicitly push the hashcode in here. This is signify the following block // is IMPORTANT: - if (klass->java_mirror() != nullptr) { - klass->java_mirror()->identity_hash(); - } + assert(klass->java_mirror() != nullptr, "must be"); + klass->java_mirror()->identity_hash(); if (!is_eligible_for_verification(klass, should_verify_class)) { return true; From 8e2bd92bacd6503346a48df236959c8a959c9c77 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 3 Feb 2026 08:41:37 +0000 Subject: [PATCH 096/215] 8376970: Shenandoah: Verifier should do basic verification before touching oops Reviewed-by: wkemper, xpeng, kdnilsen --- .../gc/shenandoah/shenandoahVerifier.cpp | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index 0cc6d4c6ed4..b60f8128d1d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -110,15 +110,15 @@ private: void do_oop_work(T* p) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); + // Basic verification should happen before we touch anything else. + // For performance reasons, only fully verify non-marked field values. + // We are here when the host object for *p is already marked. + 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); } - // Single threaded verification can use faster non-atomic stack and bitmap - // methods. - // - // For performance reasons, only fully verify non-marked field values. - // We are here when the host object for *p is already marked. if (in_generation(obj) && _map->par_mark(obj)) { verify_oop_at(p, obj); _stack->push(ShenandoahVerifierTask(obj)); @@ -131,7 +131,7 @@ private: return _generation->contains(region); } - void verify_oop(oop obj) { + void verify_oop(oop obj, bool basic = false) { // Perform consistency checks with gradually decreasing safety level. This guarantees // that failure report would not try to touch something that was not yet verified to be // safe to process. @@ -174,10 +174,14 @@ private: } } + check(ShenandoahAsserts::_safe_unknown, obj, obj_reg->is_active(), + "Object should be in active region"); + // ------------ obj is safe at this point -------------- - check(ShenandoahAsserts::_safe_oop, obj, obj_reg->is_active(), - "Object should be in active region"); + if (basic) { + return; + } switch (_options._verify_liveness) { case ShenandoahVerifier::_verify_liveness_disable: @@ -331,6 +335,18 @@ public: _interior_loc = nullptr; } + /** + * Verify object with known interior reference, with only basic verification. + * @param p interior reference where the object is referenced from; can be off-heap + * @param obj verified object + */ + template + void verify_oop_at_basic(T* p, oop obj) { + _interior_loc = p; + verify_oop(obj, /* basic = */ true); + _interior_loc = nullptr; + } + /** * Verify object without known interior reference. * Useful when picking up the object at known offset in heap, @@ -1232,7 +1248,9 @@ private: void do_oop_work(T* p) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); + 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) { ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr, @@ -1252,7 +1270,9 @@ private: void do_oop_work(T* p) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); + oop obj = CompressedOops::decode_raw_not_null(o); + ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__); + ShenandoahHeap* heap = ShenandoahHeap::heap(); if (!heap->marking_context()->is_marked_or_old(obj)) { @@ -1306,7 +1326,9 @@ public: inline void work(T* p) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { - oop obj = CompressedOops::decode_not_null(o); + oop obj = CompressedOops::decode_raw_not_null(o); + ShenandoahAsserts::assert_correct(p, obj, __FILE__, __LINE__); + if (_heap->is_in_young(obj) && !_scanner->is_card_dirty((HeapWord*) p)) { ShenandoahAsserts::print_failure(ShenandoahAsserts::_safe_all, obj, p, nullptr, _message, "clean card, it should be dirty.", __FILE__, __LINE__); From 692444f071cab930d1b92bbfac79f87d0d801aab Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 3 Feb 2026 08:44:23 +0000 Subject: [PATCH 097/215] 8376969: Shenandoah: GC state getters should be inlineable Reviewed-by: wkemper, xpeng, kdnilsen --- src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp | 12 ------------ src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp | 4 ++-- .../share/gc/shenandoah/shenandoahHeap.inline.hpp | 11 +++++++++++ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index ef99bd98c93..ccfc1c036c2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -2719,18 +2719,6 @@ bool ShenandoahRegionIterator::has_next() const { return _index < _heap->num_regions(); } -char ShenandoahHeap::gc_state() const { - return _gc_state.raw_value(); -} - -bool ShenandoahHeap::is_gc_state(GCState state) const { - // If the global gc state has been changed, but hasn't yet been propagated to all threads, then - // the global gc state is the correct value. Once the gc state has been synchronized with all threads, - // _gc_state_changed will be toggled to false and we need to use the thread local state. - return _gc_state_changed ? _gc_state.is_set(state) : ShenandoahThreadLocalData::is_gc_state(state); -} - - ShenandoahLiveData* ShenandoahHeap::get_liveness_cache(uint worker_id) { #ifdef ASSERT assert(_liveness_cache != nullptr, "sanity"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 174001170f4..9240091070b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -353,7 +353,7 @@ private: public: // This returns the raw value of the singular, global gc state. - char gc_state() const; + inline char gc_state() const; // Compares the given state against either the global gc state, or the thread local state. // The global gc state may change on a safepoint and is the correct value to use until @@ -361,7 +361,7 @@ public: // compare against the thread local state). The thread local gc state may also be changed // by a handshake operation, in which case, this function continues using the updated thread // local value. - bool is_gc_state(GCState state) const; + inline bool is_gc_state(GCState state) const; // This copies the global gc state into a thread local variable for all threads. // The thread local gc state is primarily intended to support quick access at barriers. diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp index 34c279a1495..e35f116b843 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp @@ -452,6 +452,17 @@ inline bool ShenandoahHeap::in_collection_set_loc(void* p) const { return collection_set()->is_in_loc(p); } +inline char ShenandoahHeap::gc_state() const { + return _gc_state.raw_value(); +} + +inline bool ShenandoahHeap::is_gc_state(GCState state) const { + // If the global gc state has been changed, but hasn't yet been propagated to all threads, then + // the global gc state is the correct value. Once the gc state has been synchronized with all threads, + // _gc_state_changed will be toggled to false and we need to use the thread local state. + return _gc_state_changed ? _gc_state.is_set(state) : ShenandoahThreadLocalData::is_gc_state(state); +} + inline bool ShenandoahHeap::is_idle() const { return _gc_state_changed ? _gc_state.is_clear() : ShenandoahThreadLocalData::gc_state(Thread::current()) == 0; } From 5fec0f3287a64aa56e04ad7c0222dca49a0992e0 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 3 Feb 2026 08:58:57 +0000 Subject: [PATCH 098/215] 8376585: bin/update_copyright_year.sh could allow updating a specified list of files Reviewed-by: erikj --- bin/update_copyright_year.sh | 183 ++++++++++++++++++++--------------- 1 file changed, 103 insertions(+), 80 deletions(-) diff --git a/bin/update_copyright_year.sh b/bin/update_copyright_year.sh index fa7989d234b..fcdac6b935f 100644 --- a/bin/update_copyright_year.sh +++ b/bin/update_copyright_year.sh @@ -1,7 +1,7 @@ #!/bin/bash -f # -# 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,9 +23,13 @@ # questions. # -# Script to update the Copyright YEAR range in Mercurial & Git sources. +# Script to update the Copyright YEAR range in Git sources. # (Originally from xdono, Thanks!) +# To update Copyright years for changes in a specific branch, +# you use a command along these lines: +# $ git diff upstream/master... | lsdiff | cut -d '/' -f 2- | bash bin/update_copyright_year.sh -m - + #------------------------------------------------------------ copyright="Copyright" copyright_symbol="(c)" @@ -47,7 +51,7 @@ rm -f -r ${tmp} mkdir -p ${tmp} total=0 -usage="Usage: `basename "$0"` [-c company] [-y year] [-h|f]" +usage="Usage: `basename "$0"` [-c company] [-y year] [-m file] [-h|f]" Help() { # Display Help @@ -65,15 +69,18 @@ Help() echo "-b Specifies the base reference for change set lookup." echo "-f Updates the copyright for all change sets in a given year," echo " as specified by -y. Overrides -b flag." + echo "-m Read the list of modified files from the given file," + echo " use - to read from stdin" echo "-h Print this help." echo } full_year=false base_reference=master +modified_files_origin=""; # Process options -while getopts "b:c:fhy:" option; do +while getopts "b:c:fhm:y:" option; do case $option in b) # supplied base reference base_reference=${OPTARG} @@ -91,6 +98,9 @@ while getopts "b:c:fhy:" option; do y) # supplied company year year=${OPTARG} ;; + m) # modified files will be read from the given origin + modified_files_origin="${OPTARG}" + ;; \?) # illegal option echo "$usage" exit 1 @@ -110,18 +120,10 @@ git status &> /dev/null && git_found=true if [ "$git_found" != "true" ]; then echo "Error: Please execute script from within a JDK git repository." exit 1 -else - echo "Using Git version control system" - vcs_status=(git ls-files -m) - if [ "$full_year" = "true" ]; then - vcs_list_changesets=(git log --no-merges --since="${year}-01-01T00:00:00Z" --until="${year}-12-31T23:59:59Z" --pretty=tformat:"%H") - else - vcs_list_changesets=(git log --no-merges "${base_reference}..HEAD" --since="${year}-01-01T00:00:00Z" --until="${year}-12-31T23:59:59Z" --pretty=tformat:"%H") - fi - vcs_changeset_message=(git log -1 --pretty=tformat:"%B") # followed by ${changeset} - vcs_changeset_files=(git diff-tree --no-commit-id --name-only -r) # followed by ${changeset} fi +echo "Using Git version control system" + # Return true if it makes sense to edit this file saneFileToCheck() { @@ -168,6 +170,25 @@ updateFile() # file echo "${changed}" } +# Update the copyright year on files sent in stdin +updateFiles() # stdin: list of files to update +{ + count=0 + fcount=0 + while read i; do + fcount=`expr ${fcount} '+' 1` + if [ `updateFile "${i}"` = "true" ] ; then + count=`expr ${count} '+' 1` + fi + done + if [ ${count} -gt 0 ] ; then + printf " UPDATED year on %d of %d files.\n" ${count} ${fcount} + total=`expr ${total} '+' ${count}` + else + printf " None of the %d files were changed.\n" ${fcount} + fi +} + # Update the copyright year on all files changed by this changeset updateChangesetFiles() # changeset { @@ -178,18 +199,7 @@ updateChangesetFiles() # changeset | ${awk} -F' ' '{for(i=1;i<=NF;i++)print $i}' \ > ${files} if [ -f "${files}" -a -s "${files}" ] ; then - fcount=`cat ${files}| wc -l` - for i in `cat ${files}` ; do - if [ `updateFile "${i}"` = "true" ] ; then - count=`expr ${count} '+' 1` - fi - done - if [ ${count} -gt 0 ] ; then - printf " UPDATED year on %d of %d files.\n" ${count} ${fcount} - total=`expr ${total} '+' ${count}` - else - printf " None of the %d files were changed.\n" ${fcount} - fi + cat ${files} | updateFiles else printf " ERROR: No files changed in the changeset? Must be a mistake.\n" set -x @@ -204,67 +214,80 @@ updateChangesetFiles() # changeset } # Check if repository is clean +vcs_status=(git ls-files -m) previous=`"${vcs_status[@]}"|wc -l` if [ ${previous} -ne 0 ] ; then echo "WARNING: This repository contains previously edited working set files." echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" fi -# Get all changesets this year -all_changesets=${tmp}/all_changesets -rm -f ${all_changesets} -"${vcs_list_changesets[@]}" > ${all_changesets} - -# Check changeset to see if it is Copyright only changes, filter changesets -if [ -s ${all_changesets} ] ; then - echo "Changesets made in ${year}: `cat ${all_changesets} | wc -l`" - index=0 - cat ${all_changesets} | while read changeset ; do - index=`expr ${index} '+' 1` - desc=${tmp}/desc.${changeset} - rm -f ${desc} - echo "------------------------------------------------" - "${vcs_changeset_message[@]}" "${changeset}" > ${desc} - printf "%d: %s\n%s\n" ${index} "${changeset}" "`cat ${desc}|head -1`" - if [ "${year}" = "2010" ] ; then - if cat ${desc} | grep -i -F "Added tag" > /dev/null ; then - printf " EXCLUDED tag changeset.\n" - elif cat ${desc} | grep -i -F rebrand > /dev/null ; then - printf " EXCLUDED rebrand changeset.\n" - elif cat ${desc} | grep -i -F copyright > /dev/null ; then - printf " EXCLUDED copyright changeset.\n" - else - updateChangesetFiles ${changeset} - fi - else - if cat ${desc} | grep -i -F "Added tag" > /dev/null ; then - printf " EXCLUDED tag changeset.\n" - elif cat ${desc} | grep -i -F "copyright year" > /dev/null ; then - printf " EXCLUDED copyright year changeset.\n" - else - updateChangesetFiles ${changeset} - fi - fi - rm -f ${desc} - done -fi - -if [ ${total} -gt 0 ] ; then - echo "---------------------------------------------" - echo "Updated the copyright year on a total of ${total} files." - if [ ${previous} -eq 0 ] ; then - echo "This count should match the count of modified files in the repository: ${vcs_status[*]}" - else - echo "WARNING: This repository contained previously edited working set files." - fi - echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" +if [ "x$modified_files_origin" != "x" ]; then + cat $modified_files_origin | updateFiles else - echo "---------------------------------------------" - echo "No files were changed" - if [ ${previous} -ne 0 ] ; then - echo "WARNING: This repository contained previously edited working set files." - fi - echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" + # Get all changesets this year + if [ "$full_year" = "true" ]; then + vcs_list_changesets=(git log --no-merges --since="${year}-01-01T00:00:00Z" --until="${year}-12-31T23:59:59Z" --pretty=tformat:"%H") + else + vcs_list_changesets=(git log --no-merges "${base_reference}..HEAD" --since="${year}-01-01T00:00:00Z" --until="${year}-12-31T23:59:59Z" --pretty=tformat:"%H") + fi + vcs_changeset_message=(git log -1 --pretty=tformat:"%B") # followed by ${changeset} + vcs_changeset_files=(git diff-tree --no-commit-id --name-only -r) # followed by ${changeset} + + all_changesets=${tmp}/all_changesets + rm -f ${all_changesets} + "${vcs_list_changesets[@]}" > ${all_changesets} + + # Check changeset to see if it is Copyright only changes, filter changesets + if [ -s ${all_changesets} ] ; then + echo "Changesets made in ${year}: `cat ${all_changesets} | wc -l`" + index=0 + cat ${all_changesets} | while read changeset ; do + index=`expr ${index} '+' 1` + desc=${tmp}/desc.${changeset} + rm -f ${desc} + echo "------------------------------------------------" + "${vcs_changeset_message[@]}" "${changeset}" > ${desc} + printf "%d: %s\n%s\n" ${index} "${changeset}" "`cat ${desc}|head -1`" + if [ "${year}" = "2010" ] ; then + if cat ${desc} | grep -i -F "Added tag" > /dev/null ; then + printf " EXCLUDED tag changeset.\n" + elif cat ${desc} | grep -i -F rebrand > /dev/null ; then + printf " EXCLUDED rebrand changeset.\n" + elif cat ${desc} | grep -i -F copyright > /dev/null ; then + printf " EXCLUDED copyright changeset.\n" + else + updateChangesetFiles ${changeset} + fi + else + if cat ${desc} | grep -i -F "Added tag" > /dev/null ; then + printf " EXCLUDED tag changeset.\n" + elif cat ${desc} | grep -i -F "copyright year" > /dev/null ; then + printf " EXCLUDED copyright year changeset.\n" + else + updateChangesetFiles ${changeset} + fi + fi + rm -f ${desc} + done + fi + + if [ ${total} -gt 0 ] ; then + echo "---------------------------------------------" + echo "Updated the copyright year on a total of ${total} files." + if [ ${previous} -eq 0 ] ; then + echo "This count should match the count of modified files in the repository: ${vcs_status[*]}" + else + echo "WARNING: This repository contained previously edited working set files." + fi + echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" + else + echo "---------------------------------------------" + echo "No files were changed" + if [ ${previous} -ne 0 ] ; then + echo "WARNING: This repository contained previously edited working set files." + fi + echo " ${vcs_status[*]} | wc -l = `"${vcs_status[@]}" | wc -l`" + fi fi # Cleanup From f43fbf08231a0ecf5c495c807302a851208c0736 Mon Sep 17 00:00:00 2001 From: Casper Norrbin Date: Tue, 3 Feb 2026 09:19:15 +0000 Subject: [PATCH 099/215] 8367332: Replace BlockTree tree logic with an intrusive red-black tree Reviewed-by: jsjolen, stuefe --- .../share/memory/metaspace/blockTree.cpp | 183 +++--------- .../share/memory/metaspace/blockTree.hpp | 263 ++++-------------- .../gtest/metaspace/test_blocktree.cpp | 2 +- 3 files changed, 96 insertions(+), 352 deletions(-) diff --git a/src/hotspot/share/memory/metaspace/blockTree.cpp b/src/hotspot/share/memory/metaspace/blockTree.cpp index 7ad24353c96..bdae317a0b9 100644 --- a/src/hotspot/share/memory/metaspace/blockTree.cpp +++ b/src/hotspot/share/memory/metaspace/blockTree.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. * Copyright (c) 2020, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,18 +39,14 @@ const size_t BlockTree::MinWordSize; #define NODE_FORMAT \ "@" PTR_FORMAT \ ": canary " INTPTR_FORMAT \ - ", parent " PTR_FORMAT \ - ", left " PTR_FORMAT \ - ", right " PTR_FORMAT \ + ", tree " PTR_FORMAT \ ", next " PTR_FORMAT \ ", size %zu" #define NODE_FORMAT_ARGS(n) \ p2i(n), \ (n)->_canary, \ - p2i((n)->_parent), \ - p2i((n)->_left), \ - p2i((n)->_right), \ + p2i(&(n)->_tree_node), \ p2i((n)->_next), \ (n)->_word_size @@ -74,15 +70,6 @@ const size_t BlockTree::MinWordSize; #define tree_assert_invalid_node(cond, failure_node) \ tree_assert(cond, "Invalid node: " NODE_FORMAT, NODE_FORMAT_ARGS(failure_node)) -// walkinfo keeps a node plus the size corridor it and its children -// are supposed to be in. -struct BlockTree::walkinfo { - BlockTree::Node* n; - int depth; - size_t lim1; // ( - size_t lim2; // ) -}; - // Helper for verify() void BlockTree::verify_node_pointer(const Node* n) const { tree_assert(os::is_readable_pointer(n), @@ -98,80 +85,32 @@ void BlockTree::verify_node_pointer(const Node* n) const { void BlockTree::verify() const { // Traverse the tree and test that all nodes are in the correct order. - MemRangeCounter counter; - if (_root != nullptr) { - ResourceMark rm; - GrowableArray stack; + // Verifies node ordering (n1 < n2 => word_size1 < word_size2), + // node validity, and that the tree is balanced and not ill-formed. + _tree.verify_self([&](const TreeNode* tree_node) { + const Node* n = Node::cast_to_node(tree_node); - walkinfo info; - info.n = _root; - info.lim1 = 0; - info.lim2 = SIZE_MAX; - info.depth = 0; + verify_node_pointer(n); - stack.push(info); + counter.add(n->_word_size); - while (stack.length() > 0) { - info = stack.pop(); - const Node* n = info.n; + tree_assert_invalid_node(n->_word_size >= MinWordSize, n); + tree_assert_invalid_node(n->_word_size <= chunklevel::MAX_CHUNK_WORD_SIZE, n); - verify_node_pointer(n); - - // Assume a (ridiculously large) edge limit to catch cases - // of badly degenerated or circular trees. - tree_assert(info.depth < 10000, "too deep (%d)", info.depth); - counter.add(n->_word_size); - - if (n == _root) { - tree_assert_invalid_node(n->_parent == nullptr, n); - } else { - tree_assert_invalid_node(n->_parent != nullptr, n); - } - - // check size and ordering - tree_assert_invalid_node(n->_word_size >= MinWordSize, n); - tree_assert_invalid_node(n->_word_size <= chunklevel::MAX_CHUNK_WORD_SIZE, n); - tree_assert_invalid_node(n->_word_size > info.lim1, n); - tree_assert_invalid_node(n->_word_size < info.lim2, n); - - // Check children - if (n->_left != nullptr) { - tree_assert_invalid_node(n->_left != n, n); - tree_assert_invalid_node(n->_left->_parent == n, n); - - walkinfo info2; - info2.n = n->_left; - info2.lim1 = info.lim1; - info2.lim2 = n->_word_size; - info2.depth = info.depth + 1; - stack.push(info2); - } - - if (n->_right != nullptr) { - tree_assert_invalid_node(n->_right != n, n); - tree_assert_invalid_node(n->_right->_parent == n, n); - - walkinfo info2; - info2.n = n->_right; - info2.lim1 = n->_word_size; - info2.lim2 = info.lim2; - info2.depth = info.depth + 1; - stack.push(info2); - } - - // If node has same-sized siblings check those too. - const Node* n2 = n->_next; - while (n2 != nullptr) { - verify_node_pointer(n2); - tree_assert_invalid_node(n2 != n, n2); // catch simple circles - tree_assert_invalid_node(n2->_word_size == n->_word_size, n2); - counter.add(n2->_word_size); - n2 = n2->_next; - } + // If node has same-sized siblings check those too. + const Node* n2 = n->_next; + while (n2 != nullptr) { + verify_node_pointer(n2); + tree_assert_invalid_node(n2 != n, n2); // catch simple circles + tree_assert_invalid_node(n2->_word_size == n->_word_size, n2); + counter.add(n2->_word_size); + n2 = n2->_next; } - } + + return true; + }); // At the end, check that counters match // (which also verifies that we visited every node, or at least @@ -189,64 +128,34 @@ void BlockTree::print_tree(outputStream* st) const { // as a quasi list is much clearer to the eye. // We print the tree depth-first, with stacked nodes below normal ones // (normal "real" nodes are marked with a leading '+') - if (_root != nullptr) { + if (is_empty()) { + st->print_cr(""); + return; + } - ResourceMark rm; - GrowableArray stack; + _tree.print_on(st, [&](outputStream *st, const TreeNode *tree_node, int depth) { + const Node* n = Node::cast_to_node(tree_node); - walkinfo info; - info.n = _root; - info.depth = 0; - - stack.push(info); - while (stack.length() > 0) { - info = stack.pop(); - const Node* n = info.n; - - // Print node. - st->print("%4d + ", info.depth); - if (os::is_readable_pointer(n)) { - st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n)); - } else { - st->print_cr("@" PTR_FORMAT ": unreadable (skipping subtree)", p2i(n)); - continue; // don't print this subtree - } - - // Print same-sized-nodes stacked under this node - for (Node* n2 = n->_next; n2 != nullptr; n2 = n2->_next) { - st->print_raw(" "); - if (os::is_readable_pointer(n2)) { - st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n2)); - } else { - st->print_cr("@" PTR_FORMAT ": unreadable (skipping rest of chain).", p2i(n2)); - break; // stop printing this chain. - } - } - - // Handle simple circularities - if (n == n->_right || n == n->_left || n == n->_next) { - st->print_cr("@" PTR_FORMAT ": circularity detected.", p2i(n)); - return; // stop printing - } - - // Handle children. - if (n->_right != nullptr) { - walkinfo info2; - info2.n = n->_right; - info2.depth = info.depth + 1; - stack.push(info2); - } - if (n->_left != nullptr) { - walkinfo info2; - info2.n = n->_left; - info2.depth = info.depth + 1; - stack.push(info2); - } + // Print node. + st->print("%4d + ", depth); + if (os::is_readable_pointer(n)) { + st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n)); + } else { + st->print_cr("@" PTR_FORMAT ": unreadable", p2i(n)); + return; } - } else { - st->print_cr(""); - } + // Print same-sized-nodes stacked under this node + for (Node* n2 = n->_next; n2 != nullptr; n2 = n2->_next) { + st->print_raw(" "); + if (os::is_readable_pointer(n2)) { + st->print_cr(NODE_FORMAT, NODE_FORMAT_ARGS(n2)); + } else { + st->print_cr("@" PTR_FORMAT ": unreadable (skipping rest of chain).", p2i(n2)); + break; // stop printing this chain. + } + } + }); } #endif // ASSERT diff --git a/src/hotspot/share/memory/metaspace/blockTree.hpp b/src/hotspot/share/memory/metaspace/blockTree.hpp index a01f60b166f..e7c1edf9c4f 100644 --- a/src/hotspot/share/memory/metaspace/blockTree.hpp +++ b/src/hotspot/share/memory/metaspace/blockTree.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 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -32,17 +32,18 @@ #include "memory/metaspace/metablock.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/rbTree.inline.hpp" namespace metaspace { -// BlockTree is a rather simple binary search tree. It is used to -// manage medium to large free memory blocks. +// BlockTree is tree built on an intrusive red-black tree. +// It is used to manage medium to large free memory blocks. // // There is no separation between payload (managed blocks) and nodes: the // memory blocks themselves are the nodes, with the block size being the key. // // We store node pointer information in these blocks when storing them. That -// imposes a minimum size to the managed memory blocks (1 word) +// imposes a minimum size to the managed memory blocks (1 MinWordSize) // // We want to manage many memory blocks of the same size, but we want // to prevent the tree from blowing up and degenerating into a list. Therefore @@ -53,9 +54,9 @@ namespace metaspace { // | 100 | // +-----+ // / \ -// +-----+ -// | 80 | -// +-----+ +// +-----+ +-----+ +// | 80 | | 120 | +// +-----+ +-----+ // / | \ // / +-----+ \ // +-----+ | 80 | +-----+ @@ -65,16 +66,11 @@ namespace metaspace { // | 80 | // +-----+ // -// -// Todo: This tree is unbalanced. It would be a good fit for a red-black tree. -// In order to make this a red-black tree, we need an algorithm which can deal -// with nodes which are their own payload (most red-black tree implementations -// swap payloads of their nodes at some point, see e.g. j.u.TreeSet). -// A good example is the Linux kernel rbtree, which is a clean, easy-to-read -// implementation. class BlockTree: public CHeapObj { + using TreeNode = IntrusiveRBNode; + struct Node { static const intptr_t _canary_value = @@ -86,29 +82,27 @@ class BlockTree: public CHeapObj { // in debug. const intptr_t _canary; - // Normal tree node stuff... - // (Note: all null if this is a stacked node) - Node* _parent; - Node* _left; - Node* _right; + // Tree node for linking blocks in the intrusive tree. + TreeNode _tree_node; // Blocks with the same size are put in a list with this node as head. Node* _next; // Word size of node. Note that size cannot be larger than max metaspace size, - // so this could be very well a 32bit value (in case we ever make this a balancing - // tree and need additional space for weighting information). + // so this could very well be a 32bit value. const size_t _word_size; Node(size_t word_size) : _canary(_canary_value), - _parent(nullptr), - _left(nullptr), - _right(nullptr), + _tree_node{}, _next(nullptr), _word_size(word_size) {} + static Node* cast_to_node(const TreeNode* tree_node) { + return (Node*)((uintptr_t)tree_node - offset_of(Node, _tree_node)); + } + #ifdef ASSERT bool valid() const { return _canary == _canary_value && @@ -118,8 +112,23 @@ class BlockTree: public CHeapObj { #endif }; - // Needed for verify() and print_tree() - struct walkinfo; + struct TreeComparator { + static RBTreeOrdering cmp(const size_t a, const TreeNode* b) { + const size_t node_word_size = Node::cast_to_node(b)->_word_size; + + if (a < node_word_size) { return RBTreeOrdering::LT; } + if (a > node_word_size) { return RBTreeOrdering::GT; } + return RBTreeOrdering::EQ; + } + + static bool less_than(const TreeNode* a, const TreeNode* b) { + const size_t a_word_size = Node::cast_to_node(a)->_word_size; + const size_t b_word_size = Node::cast_to_node(b)->_word_size; + + if (a_word_size < b_word_size) { return true; } + return false; + } + }; #ifdef ASSERT // Run a quick check on a node; upon suspicion dive into a full tree check. @@ -134,7 +143,7 @@ public: private: - Node* _root; + IntrusiveRBTree _tree; MemRangeCounter _counter; @@ -143,7 +152,7 @@ private: assert(head->_word_size == n->_word_size, "sanity"); n->_next = head->_next; head->_next = n; - DEBUG_ONLY(n->_left = n->_right = n->_parent = nullptr;) + DEBUG_ONLY(n->_tree_node = TreeNode()); } // Given a node list starting at head, remove one of the follow up nodes from @@ -157,183 +166,6 @@ private: return n; } - // Given a node c and a node p, wire up c as left child of p. - static void set_left_child(Node* p, Node* c) { - p->_left = c; - if (c != nullptr) { - assert(c->_word_size < p->_word_size, "sanity"); - c->_parent = p; - } - } - - // Given a node c and a node p, wire up c as right child of p. - static void set_right_child(Node* p, Node* c) { - p->_right = c; - if (c != nullptr) { - assert(c->_word_size > p->_word_size, "sanity"); - c->_parent = p; - } - } - - // Given a node n, return its successor in the tree - // (node with the next-larger size). - static Node* successor(Node* n) { - Node* succ = nullptr; - if (n->_right != nullptr) { - // If there is a right child, search the left-most - // child of that child. - succ = n->_right; - while (succ->_left != nullptr) { - succ = succ->_left; - } - } else { - succ = n->_parent; - Node* n2 = n; - // As long as I am the right child of my parent, search upward - while (succ != nullptr && n2 == succ->_right) { - n2 = succ; - succ = succ->_parent; - } - } - return succ; - } - - // Given a node, replace it with a replacement node as a child for its parent. - // If the node is root and has no parent, sets it as root. - void replace_node_in_parent(Node* child, Node* replace) { - Node* parent = child->_parent; - if (parent != nullptr) { - if (parent->_left == child) { // Child is left child - set_left_child(parent, replace); - } else { - set_right_child(parent, replace); - } - } else { - assert(child == _root, "must be root"); - _root = replace; - if (replace != nullptr) { - replace->_parent = nullptr; - } - } - return; - } - - // Given a node n and an insertion point, insert n under insertion point. - void insert(Node* insertion_point, Node* n) { - assert(n->_parent == nullptr, "Sanity"); - for (;;) { - DEBUG_ONLY(check_node(insertion_point);) - if (n->_word_size == insertion_point->_word_size) { - add_to_list(n, insertion_point); // parent stays null in this case. - break; - } else if (n->_word_size > insertion_point->_word_size) { - if (insertion_point->_right == nullptr) { - set_right_child(insertion_point, n); - break; - } else { - insertion_point = insertion_point->_right; - } - } else { - if (insertion_point->_left == nullptr) { - set_left_child(insertion_point, n); - break; - } else { - insertion_point = insertion_point->_left; - } - } - } - } - - // Given a node and a wish size, search this node and all children for - // the node closest (equal or larger sized) to the size s. - Node* find_closest_fit(Node* n, size_t s) { - Node* best_match = nullptr; - while (n != nullptr) { - DEBUG_ONLY(check_node(n);) - if (n->_word_size >= s) { - best_match = n; - if (n->_word_size == s) { - break; // perfect match or max depth reached - } - n = n->_left; - } else { - n = n->_right; - } - } - return best_match; - } - - // Given a wish size, search the whole tree for a - // node closest (equal or larger sized) to the size s. - Node* find_closest_fit(size_t s) { - if (_root != nullptr) { - return find_closest_fit(_root, s); - } - return nullptr; - } - - // Given a node n, remove it from the tree and repair tree. - void remove_node_from_tree(Node* n) { - assert(n->_next == nullptr, "do not delete a node which has a non-empty list"); - - if (n->_left == nullptr && n->_right == nullptr) { - replace_node_in_parent(n, nullptr); - - } else if (n->_left == nullptr && n->_right != nullptr) { - replace_node_in_parent(n, n->_right); - - } else if (n->_left != nullptr && n->_right == nullptr) { - replace_node_in_parent(n, n->_left); - - } else { - // Node has two children. - - // 1) Find direct successor (the next larger node). - Node* succ = successor(n); - - // There has to be a successor since n->right was != null... - assert(succ != nullptr, "must be"); - - // ... and it should not have a left child since successor - // is supposed to be the next larger node, so it must be the mostleft node - // in the sub tree rooted at n->right - assert(succ->_left == nullptr, "must be"); - assert(succ->_word_size > n->_word_size, "sanity"); - - Node* successor_parent = succ->_parent; - Node* successor_right_child = succ->_right; - - // Remove successor from its parent. - if (successor_parent == n) { - - // special case: successor is a direct child of n. Has to be the right child then. - assert(n->_right == succ, "sanity"); - - // Just replace n with this successor. - replace_node_in_parent(n, succ); - - // Take over n's old left child, too. - // We keep the successor's right child. - set_left_child(succ, n->_left); - } else { - // If the successors parent is not n, we are deeper in the tree, - // the successor has to be the left child of its parent. - assert(successor_parent->_left == succ, "sanity"); - - // The right child of the successor (if there was one) replaces - // the successor at its parent's left child. - set_left_child(successor_parent, succ->_right); - - // and the successor replaces n at its parent - replace_node_in_parent(n, succ); - - // and takes over n's old children - set_left_child(succ, n->_left); - set_right_child(succ, n->_right); - } - } - } - #ifdef ASSERT void zap_block(MetaBlock block); // Helper for verify() @@ -342,7 +174,7 @@ private: public: - BlockTree() : _root(nullptr) {} + BlockTree() {} // Add a memory block to the tree. Its content will be overwritten. void add_block(MetaBlock block) { @@ -350,10 +182,12 @@ public: const size_t word_size = block.word_size(); assert(word_size >= MinWordSize, "invalid block size %zu", word_size); Node* n = new(block.base()) Node(word_size); - if (_root == nullptr) { - _root = n; - } else { - insert(_root, n); + IntrusiveRBTree::Cursor cursor = _tree.cursor(word_size); + if (cursor.found()) { + add_to_list(n, Node::cast_to_node(cursor.node())); + } + else { + _tree.insert_at_cursor(&n->_tree_node, cursor); } _counter.add(word_size); } @@ -364,9 +198,10 @@ public: assert(word_size >= MinWordSize, "invalid block size %zu", word_size); MetaBlock result; - Node* n = find_closest_fit(word_size); + TreeNode* tree_node = _tree.closest_ge(word_size); - if (n != nullptr) { + if (tree_node != nullptr) { + Node* n = Node::cast_to_node(tree_node); DEBUG_ONLY(check_node(n);) assert(n->_word_size >= word_size, "sanity"); @@ -377,7 +212,7 @@ public: // node into its place in the tree). n = remove_from_list(n); } else { - remove_node_from_tree(n); + _tree.remove(tree_node); } result = MetaBlock((MetaWord*)n, n->_word_size); @@ -395,7 +230,7 @@ public: // Returns total size, in words, of all elements. size_t total_size() const { return _counter.total_size(); } - bool is_empty() const { return _root == nullptr; } + bool is_empty() const { return _tree.size() == 0; } DEBUG_ONLY(void print_tree(outputStream* st) const;) DEBUG_ONLY(void verify() const;) diff --git a/test/hotspot/gtest/metaspace/test_blocktree.cpp b/test/hotspot/gtest/metaspace/test_blocktree.cpp index 6d1e8d2884d..3bcd79c98ad 100644 --- a/test/hotspot/gtest/metaspace/test_blocktree.cpp +++ b/test/hotspot/gtest/metaspace/test_blocktree.cpp @@ -74,7 +74,7 @@ TEST_VM(metaspace, BlockTree_basic) { MetaWord* p = nullptr; MetaWord arr[10000]; - ASSERT_LE(BlockTree::MinWordSize, (size_t)6); // Sanity check. Adjust if Node is changed. + ASSERT_LE(BlockTree::MinWordSize, (size_t)7); // Sanity check. Adjust if Node is changed. const size_t minws = BlockTree::MinWordSize; From efa16e9e5fb07088ef2e0f2509e40fd97e4141d1 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 3 Feb 2026 09:35:21 +0000 Subject: [PATCH 100/215] 8170896: TEST_BUG: java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java failed with unreferenced() not invoked after 20.0 seconds Reviewed-by: smarks, msheppar, dfuchs --- .../LeaseCheckInterval.java | 99 ++++++++++--------- .../leaseCheckInterval/SelfTerminator.java | 22 +++-- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java index 3fa349fd671..e7c990ebdbd 100644 --- a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java +++ b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/LeaseCheckInterval.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2017, 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 @@ -31,10 +31,9 @@ * may be delayed longer than expected. While this is not a spec violation * (because there are no timeliness guarantees for any of these garbage * collection-related events), the user might expect that an unreferenced() - * invocation for an object whose last client has terminated abnorally + * invocation for an object whose last client has terminated abnormally * should occur on relatively the same time order as the lease value * granted. - * @author Peter Jones * * @library ../../../testlibrary * @modules java.rmi/sun.rmi.registry @@ -47,40 +46,39 @@ import java.rmi.Remote; import java.rmi.RemoteException; -import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import java.rmi.server.Unreferenced; +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.CountDownLatch; public class LeaseCheckInterval implements Remote, Unreferenced { public static final String BINDING = "LeaseCheckInterval"; + // lease expiry time (milliseconds) private static final long LEASE_VALUE = 10000; - private static final long TIMEOUT = 20000; + // the maximum allowed duration between the lease expiration and + // the Unreferenced.unreferenced() callback method to be invoked + private static final Duration EXPECTED_MAX_DURATION = Duration.ofMinutes(1); - private Object lock = new Object(); - private boolean unreferencedInvoked = false; + // will be counted down when Unreferenced.unreferenced() is invoked + private static final CountDownLatch callbackInvocationLatch = new CountDownLatch(1); + @Override public void unreferenced() { - System.err.println("unreferenced() method invoked"); - synchronized (lock) { - unreferencedInvoked = true; - lock.notify(); - } + System.err.println("[" + Instant.now() + "] unreferenced() method invoked"); + callbackInvocationLatch.countDown(); } public static void main(String[] args) throws Exception { - - System.err.println("\nRegression test for bug 4285878\n"); - /* * Set the duration of leases granted to a very small value, so that * we can test if expirations are detected in a roughly comparable * time. */ - System.setProperty("java.rmi.dgc.leaseValue", - String.valueOf(LEASE_VALUE)); - + System.setProperty("java.rmi.dgc.leaseValue", String.valueOf(LEASE_VALUE)); + System.err.println("running test with java.rmi.dgc.leaseValue set to " + LEASE_VALUE); LeaseCheckInterval obj = new LeaseCheckInterval(); JavaVM jvm = null; @@ -90,37 +88,32 @@ public class LeaseCheckInterval implements Remote, Unreferenced { Registry localRegistry = TestLibrary.createRegistryOnEphemeralPort(); int registryPort = TestLibrary.getRegistryPort(localRegistry); - System.err.println("created local registry"); + System.err.println("created local registry on port " + registryPort); localRegistry.bind(BINDING, obj); System.err.println("bound remote object in local registry"); - synchronized (obj.lock) { - System.err.println("starting remote client VM..."); - jvm = new JavaVM("SelfTerminator", "-Drmi.registry.port=" + - registryPort, ""); - jvm.start(); - - System.err.println("waiting for unreferenced() callback..."); - obj.lock.wait(TIMEOUT); - - if (obj.unreferencedInvoked) { - System.err.println("TEST PASSED: " + - "unreferenced() invoked in timely fashion"); - } else { - throw new RuntimeException( - "TEST FAILED: unreferenced() not invoked after " + - ((double) TIMEOUT / 1000.0) + " seconds"); - } - } - - } catch (Exception e) { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new RuntimeException( - "TEST FAILED: unexpected exception: " + e.toString()); + System.err.println("starting remote client VM..."); + jvm = new JavaVM("SelfTerminator", "-Drmi.registry.port=" + registryPort, ""); + // launch the self terminating java application which will lookup + // the bound object (thus creating a lease) and then terminate itself (thus + // creating the condition for a lease expiry). + jvm.start(); + final Instant startTime = Instant.now(); + System.err.println("waiting for SelfTerminator process to complete"); + final int exitCode = jvm.waitFor(); + if (exitCode != 0) { + throw new AssertionError("SelfTerminator process exited with" + + " a non-zero exit code: " + exitCode); } + System.err.println("SelfTerminator process completed in " + + Duration.between(startTime, Instant.now()) + + ", now waiting for Unreferenced.unreferenced() callback to be invoked"); + callbackInvocationLatch.await(); + final Instant waitEndedAt = Instant.now(); + final Duration waitDuration = assertWithinExpectedTimeLimit(waitEndedAt, startTime); + System.err.println("TEST PASSED: unreferenced() invoked in timely" + + " fashion (duration=" + waitDuration + ")"); } finally { if (jvm != null) { jvm.destroy(); @@ -135,4 +128,22 @@ public class LeaseCheckInterval implements Remote, Unreferenced { } } } + + /* + * Verifies that the duration between the lease expiration and the callback + * invocation is within the expected limit. Throws an exception if the wait + * duration is larger than expected limit, else returns the actual wait duration. + */ + private static Duration assertWithinExpectedTimeLimit(final Instant waitEndedAt, + final Instant terminationStartedAt) { + + final Duration waitDuration = Duration.between(terminationStartedAt, waitEndedAt); + System.out.println("wait completed in " + waitDuration); + if (waitDuration.compareTo(EXPECTED_MAX_DURATION) > 0) { + throw new RuntimeException("Took unexpectedly long (duration=" + + waitDuration + ") to invoke Unreferenced.unreferenced()," + + " expected max duration=" + EXPECTED_MAX_DURATION); + } + return waitDuration; + } } diff --git a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java index 4875634dbb3..36ebbf05bf1 100644 --- a/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java +++ b/test/jdk/java/rmi/server/Unreferenced/leaseCheckInterval/SelfTerminator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, 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 @@ -21,26 +21,32 @@ * questions. */ -/* - * - */ - import java.rmi.Remote; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; +import java.time.Instant; public class SelfTerminator { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { try { + log("main() invoked"); int registryPort = Integer.parseInt(System.getProperty("rmi.registry.port")); Registry registry = LocateRegistry.getRegistry("", registryPort); Remote stub = registry.lookup(LeaseCheckInterval.BINDING); + log("looked up binding, now terminating the process"); Runtime.getRuntime().halt(0); - } catch (Exception e) { - e.printStackTrace(); + } catch (Throwable t) { + log("failure: " + t); + t.printStackTrace(); + throw t; // propagate any failures and fail the process } } + + private static void log(final String message) { + final Instant now = Instant.now(); + System.err.println("[" + now + "] " + SelfTerminator.class.getName() + " - " + message); + } } From 9c83dff811c038ba8b20a9781ea3ac0f4f95b1b9 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Tue, 3 Feb 2026 09:44:00 +0000 Subject: [PATCH 101/215] 8376284: New test serviceability/sa/TestJhsdbJstackMixedCore.java from JDK-8374482 fails on Linux Alpine Reviewed-by: cjplummer, mbaesken --- .../linux/amd64/LinuxAMD64CFrame.java | 5 +- .../sa/TestJhsdbJstackMixedCore.java | 16 ++- test/lib/jdk/test/lib/SA/SATestUtils.java | 114 +++++++++++++++++- 3 files changed, 130 insertions(+), 5 deletions(-) diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java index 4bf6a0305a3..0ec7f1949bd 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java @@ -113,12 +113,13 @@ public final class LinuxAMD64CFrame extends BasicCFrame { // override base class impl to avoid ELF parsing public ClosestSymbol closestSymbolToPC() { Address symAddr = use1ByteBeforeToLookup ? pc().addOffsetTo(-1) : pc(); + var sym = dbg.lookup(dbg.getAddressValue(symAddr)); // Returns a special symbol if the address is signal handler, // otherwise returns closest symbol generated by LinuxDebugger. return dbg.isSignalTrampoline(symAddr) - ? new ClosestSymbol("", 0) - : dbg.lookup(dbg.getAddressValue(symAddr)); + ? new ClosestSymbol(sym.getName() + " ", 0) + : sym; } public Address pc() { diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java index b8b19c743e9..9fc304d1854 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java @@ -30,9 +30,11 @@ import jdk.test.lib.apps.LingeredApp; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.util.CoreUtils; +import jtreg.SkippedException; + /** * @test - * @bug 8374482 + * @bug 8374482 8376284 * @requires (os.family == "linux") & (vm.hasSA) * @requires os.arch == "amd64" * @library /test/lib @@ -59,11 +61,21 @@ public class TestJhsdbJstackMixedCore { System.out.println(out.getStdout()); System.err.println(out.getStderr()); - out.shouldContain(""); + out.shouldContain("__restore_rt "); out.shouldContain("Java_jdk_test_lib_apps_LingeredApp_crash"); } public static void main(String... args) throws Throwable { + // Check whether the symbol of signal trampoline is available. + var libc = SATestUtils.getLibCPath(); + + // SA distinguishes the frame is signal trampoline if the function + // is named "__restore_rt". + // SA cannot unwind problematic frame from it if the symbol not found. + if (!SATestUtils.isSymbolAvailable(libc, "__restore_rt")) { + throw new SkippedException("Signal trampoline (__restore_rt) not found in libc."); + } + LingeredApp app = new LingeredApp(); app.setForceCrash(true); LingeredApp.startApp(app, CoreUtils.getAlwaysPretouchArg(true)); diff --git a/test/lib/jdk/test/lib/SA/SATestUtils.java b/test/lib/jdk/test/lib/SA/SATestUtils.java index 01233b5bf55..63f522b3d62 100644 --- a/test/lib/jdk/test/lib/SA/SATestUtils.java +++ b/test/lib/jdk/test/lib/SA/SATestUtils.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 @@ -26,8 +26,15 @@ import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.Platform; import jtreg.SkippedException; +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -259,4 +266,109 @@ public class SATestUtils { throw new SkippedException("Cannot run this test on OSX if adding privileges is required."); } } + + /** + * Find library file that provides strlen(3), then returns it as libc. + * This method works on Linux only. + * @return path to libc + */ + @SuppressWarnings("restricted") + public static String getLibCPath() { + var linker = Linker.nativeLinker(); + var ptrStrlen = linker.defaultLookup() + .findOrThrow("strlen"); + var strlen = linker.downcallHandle( + ptrStrlen, + FunctionDescriptor.of(linker.canonicalLayouts().get("size_t"), ValueLayout.ADDRESS) + ); + var dladdr = linker.downcallHandle( + linker.defaultLookup().findOrThrow("dladdr"), + FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS) + ); + + var structDLInfo = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("dli_fname"), + ValueLayout.ADDRESS.withName("dli_fbase"), + ValueLayout.ADDRESS.withName("dli_sname"), + ValueLayout.ADDRESS.withName("dli_saddr") + ).withName("Dl_info"); + var hndDliFname = structDLInfo.varHandle(MemoryLayout.PathElement.groupElement("dli_fname")); + + try(var arena = Arena.ofConfined()){ + var info = arena.allocate(structDLInfo); + int result = (int)dladdr.invoke(ptrStrlen, info); + if (result == 0) { + throw new RuntimeException("dladdr() returns zero"); + } + + var ptrDliFname = (MemorySegment)hndDliFname.get(info, 0); + var libcPathLen = (long)strlen.invoke(ptrDliFname); + return ptrDliFname.reinterpret(libcPathLen + 1) // +1 for NUL + .getString(0); + } catch (Throwable t) { + throw new RuntimeException("getLibCPath() failed due to Throwable.", t); + } + } + + /** + * Find debuginfo file for the library. + * This method will work on Linux only. + * "readelf" has to be available. + * @return null if debuginfo is not available. + */ + public static String getDebugInfo(String lib) { + try { + // Attempt to find debuginfo in /usr/lib/debug + Path debuginfoPath = Path.of("/usr/lib/debug", lib + ".debug"); + boolean exists = Files.exists(debuginfoPath); + if (!exists) { + // Attempt to find debuginfo with build ID + var proc = (new ProcessBuilder("readelf", "-n", lib)).start(); + try (var reader = proc.inputReader()) { + var buildID = reader.lines() + .filter(l -> l.contains("Build ID:")) + .findAny() + .map(l -> l.replace("Build ID:", "").trim()) + .get(); + String dir = buildID.substring(0, 2); + String file = buildID.substring(2); + debuginfoPath = Path.of("/usr/lib/debug/.build_id", dir, file + ".debug"); + exists = Files.exists(debuginfoPath); + } + } + return exists ? debuginfoPath.toString() : null; + } catch (IOException e) { + throw new RuntimeException("getDebugInfo() failed due to IOException.", e); + } + } + + private static boolean isSymbolAvailableInternal(String lib, String symbol) throws IOException { + var proc = (new ProcessBuilder("nm", lib)).start(); + try (var reader = proc.inputReader()) { + return reader.lines() + .anyMatch(l -> l.endsWith(" " + symbol)); + } + } + + /** + * This method will work on Linux only. + * Both "readelf" and "nm" have to be available. + * @return true if given symbol is available in given lib. + */ + public static boolean isSymbolAvailable(String lib, String symbol) { + try { + // Attempt to find symbol from lib + boolean result = isSymbolAvailableInternal(lib, symbol); + if (!result) { + // Attempt to find symbol from debuginfo + String debuginfoPath = getDebugInfo(lib); + if (debuginfoPath != null) { + result = isSymbolAvailableInternal(debuginfoPath, symbol); + } + } + return result; + } catch (IOException e) { + throw new RuntimeException("isSymbolAvailable() failed due to IOException.", e); + } + } } From 88f538f114faf62e5decc48ae624b1c1302db13a Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Tue, 3 Feb 2026 10:46:38 +0000 Subject: [PATCH 102/215] 8376324: [IR Framework] Name methods in a CompileCommand-friendly way Reviewed-by: chagedorn, dfenacci --- .../report/FailureMessageBuilder.java | 5 +- .../tests/TestCompileThreshold.java | 6 +- .../ir_framework/tests/TestIRMatching.java | 326 ++++++++---------- .../ir_framework/tests/TestRunTests.java | 4 +- 4 files changed, 160 insertions(+), 181 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/report/FailureMessageBuilder.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/report/FailureMessageBuilder.java index 9cbba83e90b..f52c5f8fb5f 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/report/FailureMessageBuilder.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/report/FailureMessageBuilder.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 @@ -82,7 +82,8 @@ public class FailureMessageBuilder implements MatchResultVisitor { msg.append(System.lineSeparator()); } msg.append(methodIndex).append(") "); - msg.append("Method \"").append(method) + msg.append("Method \"") + .append(method.getDeclaringClass().getTypeName()).append("::").append(method.getName()) .append("\" - [Failed IR rules: ").append(failedIRRules).append("]:") .append(System.lineSeparator()); } diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestCompileThreshold.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestCompileThreshold.java index 7225c7fff07..793ef1ebe34 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestCompileThreshold.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestCompileThreshold.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 @@ -50,7 +50,7 @@ public class TestCompileThreshold { "-DPreferCommandLineFlags=true"); } catch (IRViolationException e) { Asserts.assertTrue(e.getExceptionInfo().contains("Failed IR Rules (1)"), "exactly one rule failed"); - Asserts.assertTrue(e.getExceptionInfo().contains("testWithCompileThreshold()"), + Asserts.assertTrue(e.getExceptionInfo().contains("testWithCompileThreshold"), "testWithCompileThreshold() failed"); } @@ -59,7 +59,7 @@ public class TestCompileThreshold { "-DTest=testWithoutCompileThreshold"); } catch (IRViolationException e) { Asserts.assertTrue(e.getExceptionInfo().contains("Failed IR Rules (1)"), "exactly one rule failed"); - Asserts.assertTrue(e.getExceptionInfo().contains("testWithoutCompileThreshold()"), + Asserts.assertTrue(e.getExceptionInfo().contains("testWithoutCompileThreshold"), "testWithoutCompileThreshold() failed"); } } 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 07a6a03f2a6..b6881fede75 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestIRMatching.java @@ -34,6 +34,7 @@ import java.io.PrintStream; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; /* * @test @@ -72,180 +73,180 @@ public class TestIRMatching { runWithArguments(GoodCount.class, "-XX:TLABRefillWasteFraction=50"); runWithArguments(MultipleFailOnGood.class, "-XX:TLABRefillWasteFraction=50"); - runCheck(new String[] {"-XX:TLABRefillWasteFraction=50", "-XX:+UsePerfData", "-XX:+UseTLAB"}, BadFailOnConstraint.create(AndOr1.class, "test1(int)", 1, "CallStaticJava")); - runCheck(new String[] {"-XX:TLABRefillWasteFraction=50", "-XX:-UsePerfData", "-XX:+UseTLAB"}, BadFailOnConstraint.create(AndOr1.class, "test2()", 1, "CallStaticJava")); + runCheck(new String[] {"-XX:TLABRefillWasteFraction=50", "-XX:+UsePerfData", "-XX:+UseTLAB"}, BadFailOnConstraint.create(AndOr1.class, "test1", 1, "CallStaticJava")); + runCheck(new String[] {"-XX:TLABRefillWasteFraction=50", "-XX:-UsePerfData", "-XX:+UseTLAB"}, BadFailOnConstraint.create(AndOr1.class, "test2", 1, "CallStaticJava")); - runCheck(BadFailOnConstraint.create(MultipleFailOnBad.class, "fail1()", 1, 1, "Store"), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail1()", 1, 3, "Store"), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail1()", 1, 2, 4), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail2()", 1, 1), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail2()", 1, 2, "CallStaticJava"), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail3()", 1, 2, "Store"), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail3()", 1, 1, 3), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail4()", 1, 1, "Store"), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail4()", 1, 2, 3), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail5()", 1, 1, "Store"), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail5()", 1, 2, 3), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail6()", 1, 1), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail6()", 1, 2, "MyClass"), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail6()", 1, 3, "CallStaticJava"), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail7()", 1, 1), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail7()", 1, 2, "MyClass"), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail8()", 1, 1), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail8()", 1, 2, "MyClass"), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail9()", 1, 1, "Store"), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail9()", 1, 2, "CallStaticJava"), - BadFailOnConstraint.create(MultipleFailOnBad.class, "fail10()", 1, 1, "Store", "iFld"), - GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail10()", 1, 2, 3) + runCheck(BadFailOnConstraint.create(MultipleFailOnBad.class, "fail1", 1, 1, "Store"), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail1", 1, 3, "Store"), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail1", 1, 2, 4), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail2", 1, 1), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail2", 1, 2, "CallStaticJava"), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail3", 1, 2, "Store"), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail3", 1, 1, 3), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail4", 1, 1, "Store"), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail4", 1, 2, 3), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail5", 1, 1, "Store"), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail5", 1, 2, 3), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail6", 1, 1), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail6", 1, 2, "MyClass"), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail6", 1, 3, "CallStaticJava"), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail7", 1, 1), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail7", 1, 2, "MyClass"), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail8", 1, 1), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail8", 1, 2, "MyClass"), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail9", 1, 1, "Store"), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail9", 1, 2, "CallStaticJava"), + BadFailOnConstraint.create(MultipleFailOnBad.class, "fail10", 1, 1, "Store", "iFld"), + GoodFailOnRegexConstraint.create(MultipleFailOnBad.class, "fail10", 1, 2, 3) ); - runCheck(BadCountsConstraint.create(BadCount.class, "bad1()", 1, 2, "Load"), - GoodCountsConstraint.create(BadCount.class, "bad1()", 2), - GoodCountsConstraint.create(BadCount.class, "bad2()", 1), - BadCountsConstraint.create(BadCount.class, "bad2()", 2, 2, "Store"), - BadCountsConstraint.create(BadCount.class, "bad3()", 1, 2, "Load"), - BadCountsConstraint.create(BadCount.class, "bad3()", 2, 2, "Store") + runCheck(BadCountsConstraint.create(BadCount.class, "bad1", 1, 2, "Load"), + GoodCountsConstraint.create(BadCount.class, "bad1", 2), + GoodCountsConstraint.create(BadCount.class, "bad2", 1), + BadCountsConstraint.create(BadCount.class, "bad2", 2, 2,"Store"), + BadCountsConstraint.create(BadCount.class, "bad3", 1, 2,"Load"), + BadCountsConstraint.create(BadCount.class, "bad3", 2, 2,"Store") ); - runCheck(GoodRuleConstraint.create(Calls.class, "calls()", 1), - BadFailOnConstraint.create(Calls.class, "calls()", 2, 1, "CallStaticJava", "dontInline"), - BadFailOnConstraint.create(Calls.class, "calls()", 2, 2, "CallStaticJava", "dontInline"), - GoodRuleConstraint.create(Calls.class, "calls()", 3) + runCheck(GoodRuleConstraint.create(Calls.class, "calls", 1), + BadFailOnConstraint.create(Calls.class, "calls", 2, 1, "CallStaticJava", "dontInline"), + BadFailOnConstraint.create(Calls.class, "calls", 2, 2, "CallStaticJava", "dontInline"), + GoodRuleConstraint.create(Calls.class, "calls", 3) ); - runCheck(BadFailOnConstraint.create(AllocInstance.class, "allocInstance()", 1), - BadFailOnConstraint.create(AllocInstance.class, "allocInstance()", 2), - GoodFailOnConstraint.create(AllocInstance.class, "allocInstance()", 3), - GoodFailOnConstraint.create(AllocInstance.class, "allocInstance()", 4), - GoodFailOnConstraint.create(AllocInstance.class, "allocInstance()", 5), - BadFailOnConstraint.create(AllocInstance.class, "allocInstance()", 6), - BadFailOnConstraint.create(AllocInstance.class, "allocInstance()", 7), - GoodFailOnConstraint.create(AllocInstance.class, "allocInstance()", 8), - GoodFailOnConstraint.create(AllocInstance.class, "allocInstance()", 9), - GoodFailOnConstraint.create(AllocInstance.class, "allocInstance()", 10) + runCheck(BadFailOnConstraint.create(AllocInstance.class, "allocInstance", 1), + BadFailOnConstraint.create(AllocInstance.class, "allocInstance", 2), + GoodFailOnConstraint.create(AllocInstance.class, "allocInstance", 3), + GoodFailOnConstraint.create(AllocInstance.class, "allocInstance", 4), + GoodFailOnConstraint.create(AllocInstance.class, "allocInstance", 5), + BadFailOnConstraint.create(AllocInstance.class, "allocInstance", 6), + BadFailOnConstraint.create(AllocInstance.class, "allocInstance", 7), + GoodFailOnConstraint.create(AllocInstance.class, "allocInstance", 8), + GoodFailOnConstraint.create(AllocInstance.class, "allocInstance", 9), + GoodFailOnConstraint.create(AllocInstance.class, "allocInstance", 10) ); runCheck( - BadFailOnConstraint.create(AllocInstance.class, "allocNested()", 1), - BadFailOnConstraint.create(AllocInstance.class, "allocNested()", 2), - BadFailOnConstraint.create(AllocInstance.class, "allocNested()", 3) + BadFailOnConstraint.create(AllocInstance.class, "allocNested", 1), + BadFailOnConstraint.create(AllocInstance.class, "allocNested", 2), + BadFailOnConstraint.create(AllocInstance.class, "allocNested", 3) ); - runCheck(BadFailOnConstraint.create(AllocArray.class, "allocArray()", 1), - BadFailOnConstraint.create(AllocArray.class, "allocArray()", 2), - GoodFailOnConstraint.create(AllocArray.class, "allocArray()", 3), - GoodFailOnConstraint.create(AllocArray.class, "allocArray()", 4), - GoodFailOnConstraint.create(AllocArray.class, "allocArray()", 5), - BadFailOnConstraint.create(AllocArray.class, "allocArray()", 6), - BadFailOnConstraint.create(AllocArray.class, "allocArray()", 7), - GoodFailOnConstraint.create(AllocArray.class, "allocArray()", 8), - GoodFailOnConstraint.create(AllocArray.class, "allocArray()", 9), - GoodFailOnConstraint.create(AllocArray.class, "allocArray()", 10) + runCheck(BadFailOnConstraint.create(AllocArray.class, "allocArray", 1), + BadFailOnConstraint.create(AllocArray.class, "allocArray", 2), + GoodFailOnConstraint.create(AllocArray.class, "allocArray", 3), + GoodFailOnConstraint.create(AllocArray.class, "allocArray", 4), + GoodFailOnConstraint.create(AllocArray.class, "allocArray", 5), + BadFailOnConstraint.create(AllocArray.class, "allocArray", 6), + BadFailOnConstraint.create(AllocArray.class, "allocArray", 7), + GoodFailOnConstraint.create(AllocArray.class, "allocArray", 8), + GoodFailOnConstraint.create(AllocArray.class, "allocArray", 9), + GoodFailOnConstraint.create(AllocArray.class, "allocArray", 10) ); - runCheck(BadFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 1), - BadFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 2), - GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 3), - GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 4), - GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 5), - BadFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 6), - BadFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 7), - GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 8), - GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 9), - GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray()", 10) + runCheck(BadFailOnConstraint.create(AllocArray.class, "allocMultiArray", 1), + BadFailOnConstraint.create(AllocArray.class, "allocMultiArray", 2), + GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray", 3), + GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray", 4), + GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray", 5), + BadFailOnConstraint.create(AllocArray.class, "allocMultiArray", 6), + BadFailOnConstraint.create(AllocArray.class, "allocMultiArray", 7), + GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray", 8), + GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray", 9), + GoodFailOnConstraint.create(AllocArray.class, "allocMultiArray", 10) ); - runCheck(GoodRuleConstraint.create(RunTests.class, "good1()", 1), - GoodRuleConstraint.create(RunTests.class, "good1()", 2), - GoodRuleConstraint.create(RunTests.class, "good2()", 1), - GoodRuleConstraint.create(RunTests.class, "good2()", 2), - GoodRuleConstraint.create(RunTests.class, "good3(int)", 1), - BadCountsConstraint.create(RunTests.class, "bad1(int)", 1, 0), - BadFailOnConstraint.create(RunTests.class, "bad1(int)", 2, "Load") + runCheck(GoodRuleConstraint.create(RunTests.class, "good1", 1), + GoodRuleConstraint.create(RunTests.class, "good1", 2), + GoodRuleConstraint.create(RunTests.class, "good2", 1), + GoodRuleConstraint.create(RunTests.class, "good2", 2), + GoodRuleConstraint.create(RunTests.class, "good3", 1), + BadCountsConstraint.create(RunTests.class, "bad1", 1, 0), + BadFailOnConstraint.create(RunTests.class, "bad1", 2, "Load") ); runCheck(new String[] {"-XX:+IgnoreUnrecognizedVMOptions", "-XX:-UseCompressedClassPointers"}, - BadFailOnConstraint.create(Loads.class, "load()", 1, 1, "Load"), - BadFailOnConstraint.create(Loads.class, "load()", 1, 3, "LoadI"), - BadCountsConstraint.create(Loads.class, "load()", 1, 1, 0), - BadCountsConstraint.create(Loads.class, "load()", 1, 2, 1,"Load"), - GoodRuleConstraint.create(Loads.class, "load()", 2), - GoodFailOnConstraint.create(Loads.class, "load()", 3), - BadCountsConstraint.create(Loads.class, "load()", 3, 2, 2,"Store"), - BadFailOnConstraint.create(Loads.class, "load()", 4, 2, "Store"), - BadFailOnConstraint.create(Loads.class, "load()", 5, "Load"), - BadFailOnConstraint.create(Loads.class, "load()", 6, "Load"), - BadFailOnConstraint.create(Loads.class, "load()", 7, "Load"), - GoodRuleConstraint.create(Loads.class, "load()", 8), - GoodRuleConstraint.create(Loads.class, "load()", 9), - GoodRuleConstraint.create(Loads.class, "load()", 10), - BadFailOnConstraint.create(Loads.class, "loadKlass()", 1), - BadCountsConstraint.create(Loads.class, "loadKlass()", 2, 2,"Field") + BadFailOnConstraint.create(Loads.class, "load", 1, 1, "Load"), + BadFailOnConstraint.create(Loads.class, "load", 1, 3, "LoadI"), + BadCountsConstraint.create(Loads.class, "load", 1, 1, 0), + BadCountsConstraint.create(Loads.class, "load", 1, 2, 1,"Load"), + GoodRuleConstraint.create(Loads.class, "load", 2), + GoodFailOnConstraint.create(Loads.class, "load", 3), + BadCountsConstraint.create(Loads.class, "load", 3, 2, 2,"Store"), + BadFailOnConstraint.create(Loads.class, "load", 4, 2, "Store"), + BadFailOnConstraint.create(Loads.class, "load", 5, "Load"), + BadFailOnConstraint.create(Loads.class, "load", 6, "Load"), + BadFailOnConstraint.create(Loads.class, "load", 7, "Load"), + GoodRuleConstraint.create(Loads.class, "load", 8), + GoodRuleConstraint.create(Loads.class, "load", 9), + GoodRuleConstraint.create(Loads.class, "load", 10), + BadFailOnConstraint.create(Loads.class, "loadKlass", 1), + BadCountsConstraint.create(Loads.class, "loadKlass", 2, 2,"Field") ); // Loops - runCheck(BadFailOnConstraint.create(Loops.class, "loop()", 1, "Loop"), - GoodRuleConstraint.create(Loops.class, "loop()", 2), - GoodRuleConstraint.create(Loops.class, "loop()", 3), - GoodRuleConstraint.create(Loops.class, "countedLoop()", 1), - BadFailOnConstraint.create(Loops.class, "countedLoop()", 2, "CountedLoop"), - GoodRuleConstraint.create(Loops.class, "countedLoop()", 3), - BadFailOnConstraint.create(Loops.class, "loopAndCountedLoop()", 1, "Loop"), - BadFailOnConstraint.create(Loops.class, "loopAndCountedLoop()", 2, "CountedLoop"), - GoodRuleConstraint.create(Loops.class, "loopAndCountedLoop()", 3), - GoodRuleConstraint.create(Loops.class, "countedLoopMain()", 1), - BadFailOnConstraint.create(Loops.class, "countedLoopMain()", 2, "CountedLoop"), - BadFailOnConstraint.create(Loops.class, "countedLoopMain()", 3, "CountedLoop", "main"), - GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 1), - GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 2), - GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled()", 3) + runCheck(BadFailOnConstraint.create(Loops.class, "loop", 1, "Loop"), + GoodRuleConstraint.create(Loops.class, "loop", 2), + GoodRuleConstraint.create(Loops.class, "loop", 3), + GoodRuleConstraint.create(Loops.class, "countedLoop", 1), + BadFailOnConstraint.create(Loops.class, "countedLoop", 2, "CountedLoop"), + GoodRuleConstraint.create(Loops.class, "countedLoop", 3), + BadFailOnConstraint.create(Loops.class, "loopAndCountedLoop", 1, "Loop"), + BadFailOnConstraint.create(Loops.class, "loopAndCountedLoop", 2, "CountedLoop"), + GoodRuleConstraint.create(Loops.class, "loopAndCountedLoop", 3), + GoodRuleConstraint.create(Loops.class, "countedLoopMain", 1), + BadFailOnConstraint.create(Loops.class, "countedLoopMain", 2, "CountedLoop"), + BadFailOnConstraint.create(Loops.class, "countedLoopMain", 3, "CountedLoop", "main"), + GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled", 1), + GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled", 2), + GoodRuleConstraint.create(Loops.class, "countedLoopUnrolled", 3) ); // Traps - runCheck(GoodRuleConstraint.create(Traps.class, "noTraps()", 1), - BadFailOnConstraint.create(Traps.class, "noTraps()", 2, "Store", "iFld"), - GoodRuleConstraint.create(Traps.class, "noTraps()", 3), - BadFailOnConstraint.create(Traps.class, "predicateTrap()", 1, "CallStaticJava", "uncommon_trap"), - BadFailOnConstraint.create(Traps.class, "predicateTrap()", 2, "CallStaticJava", "uncommon_trap", "predicate"), - GoodRuleConstraint.create(Traps.class, "predicateTrap()", 3), - GoodRuleConstraint.create(Traps.class, "predicateTrap()", 4), - BadFailOnConstraint.create(Traps.class, "nullCheck()", 1, "CallStaticJava", "uncommon_trap"), - BadFailOnConstraint.create(Traps.class, "nullCheck()", 2, "CallStaticJava", "uncommon_trap", "null_check"), - BadFailOnConstraint.create(Traps.class, "nullCheck()", 3, "uncommon_trap", "class_check"), - GoodRuleConstraint.create(Traps.class, "nullCheck()", 4), - BadFailOnConstraint.create(Traps.class, "nullAssert()", 1, "CallStaticJava", "uncommon_trap"), - BadFailOnConstraint.create(Traps.class, "nullAssert()", 2, "CallStaticJava", "uncommon_trap", "null_assert"), - BadFailOnConstraint.create(Traps.class, "nullAssert()", 3, "CallStaticJava", "uncommon_trap", "null_check"), - GoodRuleConstraint.create(Traps.class, "nullAssert()", 4), - BadFailOnConstraint.create(Traps.class, "unstableIf(boolean)", 1, "CallStaticJava", "uncommon_trap"), - BadFailOnConstraint.create(Traps.class, "unstableIf(boolean)", 2, "CallStaticJava", "uncommon_trap", "unstable_if"), - GoodRuleConstraint.create(Traps.class, "unstableIf(boolean)", 3), - BadFailOnConstraint.create(Traps.class, "classCheck()", 1, "CallStaticJava", "uncommon_trap"), - BadFailOnConstraint.create(Traps.class, "classCheck()", 2, "CallStaticJava", "uncommon_trap", "class_check"), - BadFailOnConstraint.create(Traps.class, "classCheck()", 3, "CallStaticJava", "uncommon_trap", "null_check"), - GoodRuleConstraint.create(Traps.class, "classCheck()", 4), - BadFailOnConstraint.create(Traps.class, "rangeCheck()", 1, "CallStaticJava", "uncommon_trap"), - BadFailOnConstraint.create(Traps.class, "rangeCheck()", 2, "CallStaticJava", "uncommon_trap", "range_check"), - BadFailOnConstraint.create(Traps.class, "rangeCheck()", 3, "CallStaticJava", "uncommon_trap", "null_check"), - GoodRuleConstraint.create(Traps.class, "rangeCheck()", 4), - BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 1, "CallStaticJava", "uncommon_trap"), + runCheck(GoodRuleConstraint.create(Traps.class, "noTraps", 1), + BadFailOnConstraint.create(Traps.class, "noTraps", 2, "Store", "iFld"), + GoodRuleConstraint.create(Traps.class, "noTraps", 3), + BadFailOnConstraint.create(Traps.class, "predicateTrap", 1, "CallStaticJava", "uncommon_trap"), + BadFailOnConstraint.create(Traps.class, "predicateTrap", 2, "CallStaticJava", "uncommon_trap", "predicate"), + GoodRuleConstraint.create(Traps.class, "predicateTrap", 3), + GoodRuleConstraint.create(Traps.class, "predicateTrap", 4), + BadFailOnConstraint.create(Traps.class, "nullCheck", 1, "CallStaticJava", "uncommon_trap"), + BadFailOnConstraint.create(Traps.class, "nullCheck", 2, "CallStaticJava", "uncommon_trap", "null_check"), + BadFailOnConstraint.create(Traps.class, "nullCheck", 3, "uncommon_trap", "class_check"), + GoodRuleConstraint.create(Traps.class, "nullCheck", 4), + BadFailOnConstraint.create(Traps.class, "nullAssert", 1, "CallStaticJava", "uncommon_trap"), + BadFailOnConstraint.create(Traps.class, "nullAssert", 2, "CallStaticJava", "uncommon_trap", "null_assert"), + BadFailOnConstraint.create(Traps.class, "nullAssert", 3, "CallStaticJava", "uncommon_trap", "null_check"), + GoodRuleConstraint.create(Traps.class, "nullAssert", 4), + BadFailOnConstraint.create(Traps.class, "unstableIf", 1, "CallStaticJava", "uncommon_trap"), + BadFailOnConstraint.create(Traps.class, "unstableIf", 2, "CallStaticJava", "uncommon_trap", "unstable_if"), + GoodRuleConstraint.create(Traps.class, "unstableIf", 3), + BadFailOnConstraint.create(Traps.class, "classCheck", 1, "CallStaticJava", "uncommon_trap"), + BadFailOnConstraint.create(Traps.class, "classCheck", 2, "CallStaticJava", "uncommon_trap", "class_check"), + BadFailOnConstraint.create(Traps.class, "classCheck", 3, "CallStaticJava", "uncommon_trap", "null_check"), + GoodRuleConstraint.create(Traps.class, "classCheck", 4), + BadFailOnConstraint.create(Traps.class, "rangeCheck", 1, "CallStaticJava", "uncommon_trap"), + BadFailOnConstraint.create(Traps.class, "rangeCheck", 2, "CallStaticJava", "uncommon_trap", "range_check"), + BadFailOnConstraint.create(Traps.class, "rangeCheck", 3, "CallStaticJava", "uncommon_trap", "null_check"), + GoodRuleConstraint.create(Traps.class, "rangeCheck", 4), + BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining", 1, "CallStaticJava", "uncommon_trap"), WhiteBox.getWhiteBox().isJVMCISupportedByGC() ? - BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 2, "CallStaticJava", "uncommon_trap", "intrinsic_or_type_checked_inlining") - : GoodRuleConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 2), - BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 3, "CallStaticJava", "uncommon_trap", "intrinsic"), - BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 4, "CallStaticJava", "uncommon_trap", "null_check"), - GoodRuleConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining()", 5) + BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining", 2, "CallStaticJava", "uncommon_trap", "intrinsic_or_type_checked_inlining") + : GoodRuleConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining", 2), + BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining", 3, "CallStaticJava", "uncommon_trap", "intrinsic"), + BadFailOnConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining", 4, "CallStaticJava", "uncommon_trap", "null_check"), + GoodRuleConstraint.create(Traps.class, "instrinsicOrTypeCheckedInlining", 5) ); runCheck(new String[] {"-XX:+BailoutToInterpreterForThrows"}, - BadFailOnConstraint.create(UnhandledTrap.class, "unhandled()", 1, "CallStaticJava", "uncommon_trap"), - BadFailOnConstraint.create(UnhandledTrap.class, "unhandled()", 2, "CallStaticJava", "uncommon_trap", "unhandled"), - GoodRuleConstraint.create(UnhandledTrap.class, "unhandled()", 3) + BadFailOnConstraint.create(UnhandledTrap.class, "unhandled", 1, "CallStaticJava", "uncommon_trap"), + BadFailOnConstraint.create(UnhandledTrap.class, "unhandled", 2, "CallStaticJava", "uncommon_trap", "unhandled"), + GoodRuleConstraint.create(UnhandledTrap.class, "unhandled", 3) ); - runCheck(BadFailOnConstraint.create(ScopeObj.class, "scopeObject()", 1, "ScObj")); - runCheck(BadFailOnConstraint.create(Membar.class, "membar()", 1, "MemBar")); + runCheck(BadFailOnConstraint.create(ScopeObj.class, "scopeObject", 1, "ScObj")); + runCheck(BadFailOnConstraint.create(Membar.class, "membar", 1, "MemBar")); String cmp; if (Platform.isPPC() || Platform.isX86()) { @@ -255,13 +256,13 @@ public class TestIRMatching { } else { cmp = "cmp"; } - runCheck(BadFailOnConstraint.create(CheckCastArray.class, "array(java.lang.Object[])", 1, cmp, "Constant"), - BadFailOnConstraint.create(CheckCastArray.class, "array(java.lang.Object[])", 2, 1,cmp, "Constant", "MyClass"), - BadFailOnConstraint.create(CheckCastArray.class, "array(java.lang.Object[])", 2, 2,cmp, "Constant", "ir_framework/tests/MyClass"), - GoodFailOnConstraint.create(CheckCastArray.class, "array(java.lang.Object[])", 3), + runCheck(BadFailOnConstraint.create(CheckCastArray.class, "array", 1, cmp, "Constant"), + BadFailOnConstraint.create(CheckCastArray.class, "array", 2, 1,cmp, "Constant", "MyClass"), + BadFailOnConstraint.create(CheckCastArray.class, "array", 2, 2,cmp, "Constant", "ir_framework/tests/MyClass"), + GoodFailOnConstraint.create(CheckCastArray.class, "array", 3), Platform.isS390x() ? // There is no checkcast_arraycopy stub for C2 on s390 - GoodFailOnConstraint.create(CheckCastArray.class, "arrayCopy(java.lang.Object[],java.lang.Class)", 1) - : BadFailOnConstraint.create(CheckCastArray.class, "arrayCopy(java.lang.Object[],java.lang.Class)", 1, "checkcast_arraycopy") + GoodFailOnConstraint.create(CheckCastArray.class, "arrayCopy", 1) + : BadFailOnConstraint.create(CheckCastArray.class, "arrayCopy", 1, "checkcast_arraycopy") ); try { @@ -1555,28 +1556,15 @@ abstract class Constraint { private final Class klass; protected final int ruleIdx; private final Pattern methodPattern; - private final String classAndMethod; - protected final Pattern irPattern; private final String methodName; protected boolean matched; - Constraint(Class klass, String methodName, int ruleIdx, Pattern irPattern) { - this.klass = klass; - classAndMethod = klass.getSimpleName() + "." + methodName; - this.ruleIdx = ruleIdx; - this.methodPattern = Pattern.compile(Pattern.quote(classAndMethod)); - this.irPattern = irPattern; - this.methodName = methodName; - this.matched = false; - } - // For good constraints only Constraint(Class klass, String methodName, int ruleIdx) { this.klass = klass; - classAndMethod = klass.getSimpleName() + "." + methodName; + String classAndMethod = klass.getSimpleName() + "::" + methodName; this.ruleIdx = ruleIdx; - this.methodPattern = Pattern.compile(Pattern.quote(classAndMethod)); - this.irPattern = null; + this.methodPattern = Pattern.compile("\\b" + Pattern.quote(classAndMethod) + "\\b"); this.methodName = methodName; this.matched = false; } @@ -1677,7 +1665,7 @@ abstract class RegexConstraint extends Constraint { final List matches; RegexConstraint(Class klass, String methodName, String category, boolean isGood, List matches, int ruleIdx, int... regexIndexes) { - super(klass, methodName, ruleIdx, initIRPattern(category, ruleIdx)); + super(klass, methodName, ruleIdx); this.category = category; this.regexIndexes = regexIndexes; if (category.equals("failOn")) { @@ -1706,16 +1694,6 @@ abstract class RegexConstraint extends Constraint { return super.errorPrefix() + " with \"" + category + "\""; } - private static Pattern initIRPattern(String category, int ruleIdx) { - if (category.equals("failOn")) { - return Pattern.compile("rule " + ruleIdx + ":.*\\R.*- failOn: Graph contains forbidden nodes.*\\R" + - ".*Constraint \\d+:.*\\R.*Matched forbidden node.*"); - } else { - return Pattern.compile("rule " + ruleIdx + ":.*\\R.*- counts: Graph contains wrong number of nodes:\\R" + - ".*Constraint \\d+:.*\\R.*Expected.*"); - } - } - @Override protected void checkIRRule(String irRule) { int categoryIndex = irRule.indexOf("- " + category); 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 f8d64944ff4..39dae4ae3ad 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestRunTests.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestRunTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, 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 @@ -54,7 +54,7 @@ public class TestRunTests { Asserts.fail("Should have thrown exception"); } catch (IRViolationException e) { System.setOut(oldOut); - String[] matches = { "test(int)", "test2(int)", "Failed IR Rules (2)"}; + String[] matches = { "test", "test2", "Failed IR Rules (2)"}; Arrays.stream(matches).forEach(m -> Asserts.assertTrue(e.getExceptionInfo().contains(m))); Asserts.assertEQ(e.getExceptionInfo().split("STANDALONE mode", -1).length - 1, 2); } From a5b4c0795d88db3d02d31fb4740612c6a53f7204 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Tue, 3 Feb 2026 11:59:01 +0000 Subject: [PATCH 103/215] 8376889: Enhance JfrRecorder::on_create_vm_3() assert output Reviewed-by: mdoerr, mgronlun, asteiner --- src/hotspot/share/jfr/recorder/jfrRecorder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp index 6a6da0f9b04..061e3feac6f 100644 --- a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp +++ b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp @@ -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 @@ -240,7 +240,7 @@ bool JfrRecorder::on_create_vm_2() { } bool JfrRecorder::on_create_vm_3() { - JVMTI_ONLY( assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_LIVE, "invalid init sequence"); ) + JVMTI_ONLY( assert(JvmtiEnvBase::get_phase() == JVMTI_PHASE_LIVE, "invalid init sequence, phase is %d", (int)JvmtiEnvBase::get_phase()); ) return CDSConfig::is_dumping_archive() || launch_command_line_recordings(JavaThread::current()); } From 69c3e2780c44c6ad2ef0f296e8cfba7796f2213e Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 3 Feb 2026 12:37:33 +0000 Subject: [PATCH 104/215] 8376410: G1: Task queue statistics not reset properly on mark abort Reviewed-by: shade, iwalulya --- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 14 ++++++++------ src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 4 +++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 5f096c2b9d7..8f3cafe1f5b 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -639,8 +639,7 @@ void G1ConcurrentMark::reset_marking_for_restart() { _finger = _heap.start(); for (uint i = 0; i < _max_num_tasks; ++i) { - G1CMTaskQueue* queue = _task_queues->queue(i); - queue->set_empty(); + _tasks[i]->reset_for_restart(); } } @@ -1943,11 +1942,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() { return false; } - // Empty mark stack reset_marking_for_restart(); - for (uint i = 0; i < _max_num_tasks; ++i) { - _tasks[i]->clear_region_fields(); - } abort_marking_threads(); @@ -2118,6 +2113,13 @@ void G1CMTask::reset(G1CMBitMap* mark_bitmap) { _mark_stats_cache.reset(); } +void G1CMTask::reset_for_restart() { + clear_region_fields(); + _task_queue->set_empty(); + TASKQUEUE_STATS_ONLY(_partial_array_splitter.stats()->reset()); + TASKQUEUE_STATS_ONLY(_task_queue->stats.reset()); +} + void G1CMTask::register_partial_array_splitter() { ::new (&_partial_array_splitter) PartialArraySplitter(_cm->partial_array_state_manager(), diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 3a4cbf1b83e..0271e6a4208 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -844,8 +844,10 @@ private: // Apply the closure to the given range of elements in the objArray. inline void process_array_chunk(objArrayOop obj, size_t start, size_t end); public: - // Resets the task; should be called right at the beginning of a marking phase. + // Resets the task completely for a new marking; should be called right at the beginning of a marking phase. void reset(G1CMBitMap* mark_bitmap); + // Minimal reset of the task, making it ready for continuing to mark. + void reset_for_restart(); // Register/unregister Partial Array Splitter Allocator with the PartialArrayStateManager. // This allows us to discard memory arenas used for partial object array states at the end // of a concurrent mark cycle. From 99bc98357dab78bef2cce7a10c98d13d1e5730e3 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Tue, 3 Feb 2026 13:37:51 +0000 Subject: [PATCH 105/215] 8377015: ConnectionRefusedMessage::testFinishConnect test fails on AIX with java.net.ConnectException: Connection refused Reviewed-by: alanb, mbaesken --- .../Selector/ConnectionRefusedMessage.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java b/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java index 5e7b6395a66..04490f63efe 100644 --- a/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java +++ b/test/jdk/java/nio/channels/Selector/ConnectionRefusedMessage.java @@ -69,7 +69,15 @@ class ConnectionRefusedMessage { sc.register(selector, SelectionKey.OP_CONNECT); System.err.println("establishing connection to " + destAddr); - boolean connected = sc.connect(destAddr); + boolean connected; + try { + connected = sc.connect(destAddr); + } catch (ConnectException ce) { + // Connect failed immediately, which is OK. + System.err.println("SocketChannel.connect() threw ConnectException - " + ce); + assertExceptionMessage(ce); + return; // nothing more to test + } // this test checks the exception message of a ConnectException, so it's // OK to skip the test if something unexpectedly accepted the connection assumeFalse(connected, "unexpectedly connected to " + destAddr); @@ -90,17 +98,22 @@ class ConnectionRefusedMessage { // with a return value of false fail("ConnectException was not thrown"); } catch (ConnectException ce) { - System.err.println("got (expected) ConnectException - " + ce); + System.err.println("got (expected) ConnectException from " + + "SocketChannel.finishConnect() - " + ce); // verify exception message - if (!"Connection refused".equals(ce.getMessage())) { - // propagate the original exception - fail("unexpected exception message: " + ce.getMessage(), ce); - } + assertExceptionMessage(ce); } } } } + private static void assertExceptionMessage(final ConnectException ce) { + if (!"Connection refused".equals(ce.getMessage())) { + // propagate the original exception + fail("unexpected exception message: " + ce.getMessage(), ce); + } + } + // Try to find a suitable port to provoke a "Connection Refused" error. private static InetSocketAddress findSuitableRefusedAddress() throws IOException { final InetAddress loopbackAddr = InetAddress.getLoopbackAddress(); From e51ccef9cb415ed31db70971bb439ca3d96c5bce Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Tue, 3 Feb 2026 16:32:21 +0000 Subject: [PATCH 106/215] 8347938: Add Support for the Latest ML-KEM and ML-DSA Private Key Encodings Reviewed-by: mullan, bperez, mpowers --- .../com/sun/crypto/provider/ML_KEM.java | 18 +- .../com/sun/crypto/provider/ML_KEM_Impls.java | 110 +++++-- .../sun/security/pkcs/NamedPKCS8Key.java | 121 ++++++-- .../classes/sun/security/provider/ML_DSA.java | 50 ++- .../sun/security/provider/ML_DSA_Impls.java | 110 +++++-- .../sun/security/provider/NamedKEM.java | 57 ++-- .../security/provider/NamedKeyFactory.java | 175 ++++++----- .../provider/NamedKeyPairGenerator.java | 85 ++++-- .../sun/security/provider/NamedSignature.java | 57 ++-- .../classes/sun/security/util/KeyChoices.java | 289 ++++++++++++++++++ .../sun/security/x509/NamedX509Key.java | 5 +- .../share/conf/security/java.security | 25 ++ .../sun/security/provider/acvp/Launcher.java | 4 + .../security/provider/acvp/ML_DSA_Test.java | 14 +- .../security/provider/acvp/ML_KEM_Test.java | 19 +- .../provider/{ => named}/NamedEdDSA.java | 39 ++- .../{ => named}/NamedKeyFactoryTest.java | 58 ++-- .../security/provider/named/NamedKeys.java | 103 +++++++ .../provider/pqc/PrivateKeyEncodings.java | 227 ++++++++++++++ .../security/provider/pqc/SeedOrExpanded.java | 194 ++++++++++++ test/lib/jdk/test/lib/process/Proc.java | 11 +- .../lib/security/RepositoryFileReader.java | 9 + 22 files changed, 1491 insertions(+), 289 deletions(-) create mode 100644 src/java.base/share/classes/sun/security/util/KeyChoices.java rename test/jdk/sun/security/provider/{ => named}/NamedEdDSA.java (84%) rename test/jdk/sun/security/provider/{ => named}/NamedKeyFactoryTest.java (85%) create mode 100644 test/jdk/sun/security/provider/named/NamedKeys.java create mode 100644 test/jdk/sun/security/provider/pqc/PrivateKeyEncodings.java create mode 100644 test/jdk/sun/security/provider/pqc/SeedOrExpanded.java diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java index bf6576f4d93..56a119893a7 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java @@ -498,7 +498,7 @@ public final class ML_KEM { /* Main internal algorithms from Section 6 of specification */ - protected ML_KEM_KeyPair generateKemKeyPair(byte[] kem_d, byte[] kem_z) { + protected ML_KEM_KeyPair generateKemKeyPair(byte[] kem_d_z) { MessageDigest mlKemH; try { mlKemH = MessageDigest.getInstance(HASH_H_NAME); @@ -508,7 +508,8 @@ public final class ML_KEM { } //Generate K-PKE keys - var kPkeKeyPair = generateK_PkeKeyPair(kem_d); + //The 1st 32-byte `d` is used in K-PKE key pair generation + var kPkeKeyPair = generateK_PkeKeyPair(kem_d_z); //encaps key = kPke encryption key byte[] encapsKey = kPkeKeyPair.publicKey.keyBytes; @@ -527,7 +528,8 @@ public final class ML_KEM { // This should never happen. throw new RuntimeException(e); } - System.arraycopy(kem_z, 0, decapsKey, + // The 2nd 32-byte `z` is copied into decapsKey + System.arraycopy(kem_d_z, 32, decapsKey, kPkePrivateKey.length + encapsKey.length + 32, 32); return new ML_KEM_KeyPair( @@ -535,6 +537,12 @@ public final class ML_KEM { new ML_KEM_DecapsulationKey(decapsKey)); } + public byte[] privKeyToPubKey(byte[] decapsKey) { + int pkLen = (mlKem_k * ML_KEM_N * 12) / 8 + 32 /* rho */; + int skLen = (mlKem_k * ML_KEM_N * 12) / 8; + return Arrays.copyOfRange(decapsKey, skLen, skLen + pkLen); + } + protected ML_KEM_EncapsulateResult encapsulate( ML_KEM_EncapsulationKey encapsulationKey, byte[] randomMessage) { MessageDigest mlKemH; @@ -648,10 +656,12 @@ public final class ML_KEM { throw new RuntimeException(e); } - mlKemG.update(seed); + // Note: only the 1st 32-byte in the seed is used + mlKemG.update(seed, 0, 32); mlKemG.update((byte)mlKem_k); var rhoSigma = mlKemG.digest(); + mlKemG.reset(); var rho = Arrays.copyOfRange(rhoSigma, 0, 32); var sigma = Arrays.copyOfRange(rhoSigma, 32, 64); Arrays.fill(rhoSigma, (byte)0); diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java index 2ce5b3324e7..117f26e6981 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM_Impls.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -26,9 +26,12 @@ package com.sun.crypto.provider; import sun.security.jca.JCAUtil; +import sun.security.pkcs.NamedPKCS8Key; import sun.security.provider.NamedKEM; import sun.security.provider.NamedKeyFactory; import sun.security.provider.NamedKeyPairGenerator; +import sun.security.util.KeyChoices; +import sun.security.x509.NamedX509Key; import java.security.*; import java.util.Arrays; @@ -37,6 +40,20 @@ import javax.crypto.DecapsulateException; public final class ML_KEM_Impls { + private static final int SEED_LEN = 64; + + public static byte[] seedToExpanded(String pname, byte[] seed) { + return new ML_KEM(pname).generateKemKeyPair(seed) + .decapsulationKey() + .keyBytes(); + } + + public static NamedX509Key privKeyToPubKey(NamedPKCS8Key npk) { + return new NamedX509Key(npk.getAlgorithm(), + npk.getParams().getName(), + new ML_KEM(npk.getParams().getName()).privKeyToPubKey(npk.getExpanded())); + } + public sealed static class KPG extends NamedKeyPairGenerator permits KPG2, KPG3, KPG5 { @@ -50,25 +67,27 @@ public final class ML_KEM_Impls { } @Override - protected byte[][] implGenerateKeyPair(String name, SecureRandom random) { - byte[] seed = new byte[32]; + protected byte[][] implGenerateKeyPair(String pname, SecureRandom random) { + byte[] seed = new byte[SEED_LEN]; var r = random != null ? random : JCAUtil.getDefSecureRandom(); r.nextBytes(seed); - byte[] z = new byte[32]; - r.nextBytes(z); - ML_KEM mlKem = new ML_KEM(name); + ML_KEM mlKem = new ML_KEM(pname); ML_KEM.ML_KEM_KeyPair kp; + kp = mlKem.generateKemKeyPair(seed); + var expanded = kp.decapsulationKey().keyBytes(); + try { - kp = mlKem.generateKemKeyPair(seed, z); + return new byte[][]{ + kp.encapsulationKey().keyBytes(), + KeyChoices.writeToChoice( + KeyChoices.getPreferred("mlkem"), + seed, expanded), + expanded + }; } finally { - Arrays.fill(seed, (byte)0); - Arrays.fill(z, (byte)0); + Arrays.fill(seed, (byte) 0); } - return new byte[][] { - kp.encapsulationKey().keyBytes(), - kp.decapsulationKey().keyBytes() - }; } } @@ -94,8 +113,39 @@ public final class ML_KEM_Impls { public KF() { super("ML-KEM", "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"); } - public KF(String name) { - super("ML-KEM", name); + public KF(String pname) { + super("ML-KEM", pname); + } + + @Override + protected byte[] implExpand(String pname, byte[] input) + throws InvalidKeyException { + return KeyChoices.choiceToExpanded(pname, SEED_LEN, input, + ML_KEM_Impls::seedToExpanded); + } + + @Override + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + var nk = toNamedKey(key); + if (nk instanceof NamedPKCS8Key npk) { + var type = KeyChoices.getPreferred("mlkem"); + if (KeyChoices.typeOfChoice(npk.getRawBytes()) != type) { + var encoding = KeyChoices.choiceToChoice( + type, + npk.getParams().getName(), + SEED_LEN, npk.getRawBytes(), + ML_KEM_Impls::seedToExpanded); + nk = NamedPKCS8Key.internalCreate( + npk.getAlgorithm(), + npk.getParams().getName(), + encoding, + npk.getExpanded().clone()); + if (npk != key) { // npk is neither input or output + npk.destroy(); + } + } + } + return nk; } } @@ -121,15 +171,15 @@ public final class ML_KEM_Impls { private static final int SEED_SIZE = 32; @Override - protected byte[][] implEncapsulate(String name, byte[] encapsulationKey, + protected byte[][] implEncapsulate(String pname, byte[] encapsulationKey, Object ek, SecureRandom secureRandom) { byte[] randomBytes = new byte[SEED_SIZE]; var r = secureRandom != null ? secureRandom : JCAUtil.getDefSecureRandom(); r.nextBytes(randomBytes); - ML_KEM mlKem = new ML_KEM(name); - ML_KEM.ML_KEM_EncapsulateResult mlKemEncapsulateResult = null; + ML_KEM mlKem = new ML_KEM(pname); + ML_KEM.ML_KEM_EncapsulateResult mlKemEncapsulateResult; try { mlKemEncapsulateResult = mlKem.encapsulate( new ML_KEM.ML_KEM_EncapsulationKey( @@ -145,49 +195,49 @@ public final class ML_KEM_Impls { } @Override - protected byte[] implDecapsulate(String name, byte[] decapsulationKey, + protected byte[] implDecapsulate(String pname, byte[] decapsulationKey, Object dk, byte[] cipherText) throws DecapsulateException { - ML_KEM mlKem = new ML_KEM(name); + ML_KEM mlKem = new ML_KEM(pname); var kpkeCipherText = new ML_KEM.K_PKE_CipherText(cipherText); return mlKem.decapsulate(new ML_KEM.ML_KEM_DecapsulationKey( decapsulationKey), kpkeCipherText); } @Override - protected int implSecretSize(String name) { + protected int implSecretSize(String pname) { return ML_KEM.SECRET_SIZE; } @Override - protected int implEncapsulationSize(String name) { - ML_KEM mlKem = new ML_KEM(name); + protected int implEncapsulationSize(String pname) { + ML_KEM mlKem = new ML_KEM(pname); return mlKem.getEncapsulationSize(); } @Override - protected Object implCheckPublicKey(String name, byte[] pk) + protected Object implCheckPublicKey(String pname, byte[] pk) throws InvalidKeyException { - ML_KEM mlKem = new ML_KEM(name); + ML_KEM mlKem = new ML_KEM(pname); return mlKem.checkPublicKey(pk); } @Override - protected Object implCheckPrivateKey(String name, byte[] sk) + protected Object implCheckPrivateKey(String pname, byte[] sk) throws InvalidKeyException { - ML_KEM mlKem = new ML_KEM(name); + ML_KEM mlKem = new ML_KEM(pname); return mlKem.checkPrivateKey(sk); } public K() { - super("ML-KEM", "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"); + super("ML-KEM", new KF()); } - public K(String name) { - super("ML-KEM", name); + public K(String pname) { + super("ML-KEM", new KF(pname)); } } diff --git a/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java index a748433da87..e1beb8b6b9b 100644 --- a/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java +++ b/src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java @@ -25,11 +25,8 @@ package sun.security.pkcs; -import sun.security.util.DerInputStream; -import sun.security.util.DerValue; import sun.security.x509.AlgorithmId; -import javax.security.auth.DestroyFailedException; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; @@ -39,6 +36,7 @@ import java.security.NoSuchAlgorithmException; import java.security.ProviderException; import java.security.spec.NamedParameterSpec; import java.util.Arrays; +import java.util.Objects; /// Represents a private key from an algorithm family that is specialized /// with a named parameter set. @@ -50,6 +48,28 @@ import java.util.Arrays; /// identifier in the PKCS #8 encoding of the key is always a single OID derived /// from the parameter set name. /// +/// Besides the existing [PKCS8Key#privKeyMaterial] field, this class optionally +/// supports an expanded format stored in [#expanded]. While `privKeyMaterial` +/// always represents the format used for encoding, `expanded` is always used +/// in computations. The expanded format must be self-sufficient for +/// cryptographic computations without requiring the encoding format. +/// +/// 1. If only `privKeyMaterial` is present, it's also the expanded format. +/// 2. If both `privKeyMaterial` and `expanded` are available, `privKeyMaterial` +/// is the encoding format, and `expanded` is the expanded format. +/// +/// If the two formats are the same, only `privKeyMaterial` is included, and +/// `expanded` must be `null`. Some implementations might be tempted to put the +/// same value into `privKeyMaterial` and `expanded`. However, problems can +/// arise if they happen to be the same object. To avoid ambiguity, always set +/// `expanded` to `null`. +/// +/// If the `expanded` field is required by the algorithm, it is either +/// [calculated from the PKCS #8 encoding][#NamedPKCS8Key(String, byte\[\], Expander)], +/// or [provided directly][#internalCreate(String, String, byte\[\], byte\[\])]. +/// In the latter case, the caller must ensure the consistency of the `encoded` +/// and `expanded` arguments. For example, seed and expanded key must match. +/// /// @see sun.security.provider.NamedKeyPairGenerator public final class NamedPKCS8Key extends PKCS8Key { @Serial @@ -57,42 +77,64 @@ public final class NamedPKCS8Key extends PKCS8Key { private final String fname; private final transient NamedParameterSpec paramSpec; - private final byte[] rawBytes; + private final transient byte[] expanded; private transient boolean destroyed = false; - /// Ctor from family name, parameter set name, raw key bytes. - /// Key bytes won't be cloned, caller must relinquish ownership - public NamedPKCS8Key(String fname, String pname, byte[] rawBytes) { + /// Creates a `NamedPKCS8Key` from raw components. + /// + /// @param fname family name + /// @param pname parameter set name + /// @param encoded raw key bytes, not null + /// @param expanded expanded key format, can be `null`. + private NamedPKCS8Key(String fname, String pname, byte[] encoded, byte[] expanded) { this.fname = fname; this.paramSpec = new NamedParameterSpec(pname); + this.expanded = expanded; + this.privKeyMaterial = Objects.requireNonNull(encoded); try { this.algid = AlgorithmId.get(pname); } catch (NoSuchAlgorithmException e) { throw new ProviderException(e); } - this.rawBytes = rawBytes; - - DerValue val = new DerValue(DerValue.tag_OctetString, rawBytes); - try { - this.privKeyMaterial = val.toByteArray(); - } finally { - val.clear(); - } } - /// Ctor from family name, and PKCS #8 bytes - public NamedPKCS8Key(String fname, byte[] encoded) throws InvalidKeyException { + /// Creates a `NamedPKCS8Key` from raw components. + /// + /// `encoded` and `expanded` won't be cloned, caller must relinquish + /// ownership. This caller must ensure `encoded` and `expanded` match + /// each other and `encoded` is valid and internally-consistent. + /// + /// @param fname family name + /// @param pname parameter set name + /// @param encoded raw key bytes, not null + /// @param expanded expanded key format, can be `null`. + public static NamedPKCS8Key internalCreate(String fname, String pname, + byte[] encoded, byte[] expanded) { + return new NamedPKCS8Key(fname, pname, encoded, expanded); + } + + /// Creates a `NamedPKCS8Key` from family name and PKCS #8 encoding. + /// + /// @param fname family name + /// @param encoded PKCS #8 encoding. It is copied so caller can modify + /// it after the method call. + /// @param expander a function that is able to calculate the expanded + /// format from the encoding format inside `encoded`. If it recognizes + /// the input already in expanded format, it must return `null`. + /// This argument must be `null` if the algorithm's expanded format + /// is always the same as its encoding format. Whatever the case, the + /// ownership of the result is fully granted to this object. + public NamedPKCS8Key(String fname, byte[] encoded, Expander expander) + throws InvalidKeyException { super(encoded); this.fname = fname; - try { - paramSpec = new NamedParameterSpec(algid.getName()); - if (algid.getEncodedParams() != null) { - throw new InvalidKeyException("algorithm identifier has params"); - } - rawBytes = new DerInputStream(privKeyMaterial).getOctetString(); - } catch (IOException e) { - throw new InvalidKeyException("Cannot parse input", e); + this.expanded = expander == null + ? null + : expander.expand(algid.getName(), this.privKeyMaterial); + paramSpec = new NamedParameterSpec(algid.getName()); + if (algid.getEncodedParams() != null) { + throw new InvalidKeyException("algorithm identifier has params"); } } @@ -104,9 +146,15 @@ public final class NamedPKCS8Key extends PKCS8Key { } /// Returns the reference to the internal key. Caller must not modify - /// the content or keep a reference. + /// the content or pass the reference to untrusted application code. public byte[] getRawBytes() { - return rawBytes; + return privKeyMaterial; + } + + /// Returns the reference to the key in expanded format. Caller must not + /// modify the content or pass the reference to untrusted application code. + public byte[] getExpanded() { + return expanded == null ? privKeyMaterial : expanded; } @Override @@ -127,9 +175,11 @@ public final class NamedPKCS8Key extends PKCS8Key { } @Override - public void destroy() throws DestroyFailedException { - Arrays.fill(rawBytes, (byte)0); + public void destroy() { Arrays.fill(privKeyMaterial, (byte)0); + if (expanded != null) { + Arrays.fill(expanded, (byte)0); + } if (encodedKey != null) { Arrays.fill(encodedKey, (byte)0); } @@ -140,4 +190,17 @@ public final class NamedPKCS8Key extends PKCS8Key { public boolean isDestroyed() { return destroyed; } + + /// Expands from encoding format to expanded format. + @FunctionalInterface + public interface Expander { + /// The expand method + /// + /// @param pname parameter set name + /// @param input input encoding + /// @return the expanded key, `null` if `input` is already in expanded + /// @throws InvalidKeyException if `input` is invalid, for example, + /// wrong encoding, or internal inconsistency + byte[] expand(String pname, byte[] input) throws InvalidKeyException; + } } diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA.java b/src/java.base/share/classes/sun/security/provider/ML_DSA.java index 6a578427e51..1e27349a5d0 100644 --- a/src/java.base/share/classes/sun/security/provider/ML_DSA.java +++ b/src/java.base/share/classes/sun/security/provider/ML_DSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -568,6 +568,54 @@ public class ML_DSA { return new ML_DSA_KeyPair(sk, pk); } + private static int[][] deepClone(int[][] array) { + int[][] clone = new int[array.length][]; + for (int i = 0; i < array.length; i++) { + clone[i] = array[i].clone(); + } + return clone; + } + + // This is similar to the generateKeyPairInternal method. Instead of + // generating from a seed, it uses stored fields inside the private key + // to calculate the public key. It performs several checks during the + // calculation to make sure the private key is a valid one. Otherwise, + // an IllegalArgumentException is thrown. + public ML_DSA_PublicKey privKeyToPubKey(ML_DSA_PrivateKey sk) { + // Sample A + int[][][] keygenA = generateA(sk.rho); //A is in NTT domain + + // Compute t and tr + // make a copy of sk.s1 and modify it. Although we can also + // take it out of NTT domain later, it was modified for a while. + var s1 = deepClone(sk.s1); + mlDsaVectorNtt(s1); //s1 now in NTT domain + int[][] As1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); + matrixVectorPointwiseMultiply(As1, keygenA, s1); + + mlDsaVectorInverseNtt(As1); + int[][] t = vectorAddPos(As1, sk.s2); + int[][] t0 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); + int[][] t1 = integerMatrixAlloc(mlDsa_k, ML_DSA_N); + power2Round(t, t0, t1); + if (!Arrays.deepEquals(t0, sk.t0)) { + throw new IllegalArgumentException("t0 does not patch"); + } + + var crHash = new SHAKE256(TR_LEN); + + ML_DSA_PublicKey pk = new ML_DSA_PublicKey(sk.rho, t1); + byte[] publicKeyBytes = pkEncode(pk); + crHash.update(publicKeyBytes); + byte[] tr = crHash.digest(); + if (!Arrays.equals(tr, sk.tr)) { + throw new IllegalArgumentException("tr does not patch"); + } + + //Encode PK + return new ML_DSA_PublicKey(sk.rho, t1); + } + public ML_DSA_Signature signInternal(byte[] message, byte[] rnd, byte[] skBytes) { //Decode private key and initialize hash function ML_DSA_PrivateKey sk = skDecode(skBytes); diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java b/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java index dffe7c5cdb1..730e253f407 100644 --- a/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java +++ b/src/java.base/share/classes/sun/security/provider/ML_DSA_Impls.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -26,12 +26,35 @@ package sun.security.provider; import sun.security.jca.JCAUtil; +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.util.KeyChoices; +import sun.security.x509.NamedX509Key; + import java.security.*; import java.security.SecureRandom; import java.util.Arrays; public class ML_DSA_Impls { + private static final int SEED_LEN = 32; + + public static byte[] seedToExpanded(String pname, byte[] seed) { + var impl = new ML_DSA(name2int(pname)); + var sk = impl.generateKeyPairInternal(seed).privateKey(); + try { + return impl.skEncode(sk); + } finally { + sk.destroy(); + } + } + + public static NamedX509Key privKeyToPubKey(NamedPKCS8Key npk) { + var dsa = new ML_DSA(name2int(npk.getParams().getName())); + return new NamedX509Key(npk.getAlgorithm(), + npk.getParams().getName(), + dsa.pkEncode(dsa.privKeyToPubKey(dsa.skDecode(npk.getExpanded())))); + } + public enum Version { DRAFT, FINAL } @@ -43,16 +66,16 @@ public class ML_DSA_Impls { // --add-exports java.base/sun.security.provider=ALL-UNNAMED public static Version version = Version.FINAL; - static int name2int(String name) { - if (name.endsWith("44")) { + static int name2int(String pname) { + if (pname.endsWith("44")) { return 2; - } else if (name.endsWith("65")) { + } else if (pname.endsWith("65")) { return 3; - } else if (name.endsWith("87")) { + } else if (pname.endsWith("87")) { return 5; } else { // should not happen - throw new ProviderException("Unknown name " + name); + throw new ProviderException("Unknown name " + pname); } } @@ -69,20 +92,26 @@ public class ML_DSA_Impls { } @Override - protected byte[][] implGenerateKeyPair(String name, SecureRandom sr) { - byte[] seed = new byte[32]; - var r = sr != null ? sr : JCAUtil.getDefSecureRandom(); + protected byte[][] implGenerateKeyPair(String pname, SecureRandom random) { + byte[] seed = new byte[SEED_LEN]; + var r = random != null ? random : JCAUtil.getDefSecureRandom(); r.nextBytes(seed); - ML_DSA mlDsa = new ML_DSA(name2int(name)); + + ML_DSA mlDsa = new ML_DSA(name2int(pname)); ML_DSA.ML_DSA_KeyPair kp = mlDsa.generateKeyPairInternal(seed); + var expanded = mlDsa.skEncode(kp.privateKey()); + try { return new byte[][]{ mlDsa.pkEncode(kp.publicKey()), - mlDsa.skEncode(kp.privateKey()) + KeyChoices.writeToChoice( + KeyChoices.getPreferred("mldsa"), + seed, expanded), + expanded }; } finally { kp.privateKey().destroy(); - Arrays.fill(seed, (byte)0); + Arrays.fill(seed, (byte) 0); } } } @@ -109,8 +138,39 @@ public class ML_DSA_Impls { public KF() { super("ML-DSA", "ML-DSA-44", "ML-DSA-65", "ML-DSA-87"); } - public KF(String name) { - super("ML-DSA", name); + public KF(String pname) { + super("ML-DSA", pname); + } + + @Override + protected byte[] implExpand(String pname, byte[] input) + throws InvalidKeyException { + return KeyChoices.choiceToExpanded(pname, SEED_LEN, input, + ML_DSA_Impls::seedToExpanded); + } + + @Override + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + var nk = toNamedKey(key); + if (nk instanceof NamedPKCS8Key npk) { + var type = KeyChoices.getPreferred("mldsa"); + if (KeyChoices.typeOfChoice(npk.getRawBytes()) != type) { + var encoding = KeyChoices.choiceToChoice( + type, + npk.getParams().getName(), + SEED_LEN, npk.getRawBytes(), + ML_DSA_Impls::seedToExpanded); + nk = NamedPKCS8Key.internalCreate( + npk.getAlgorithm(), + npk.getParams().getName(), + encoding, + npk.getExpanded().clone()); + if (npk != key) { // npk is neither input or output + npk.destroy(); + } + } + } + return nk; } } @@ -134,16 +194,16 @@ public class ML_DSA_Impls { public sealed static class SIG extends NamedSignature permits SIG2, SIG3, SIG5 { public SIG() { - super("ML-DSA", "ML-DSA-44", "ML-DSA-65", "ML-DSA-87"); + super("ML-DSA", new KF()); } - public SIG(String name) { - super("ML-DSA", name); + public SIG(String pname) { + super("ML-DSA", new KF(pname)); } @Override - protected byte[] implSign(String name, byte[] skBytes, + protected byte[] implSign(String pname, byte[] skBytes, Object sk2, byte[] msg, SecureRandom sr) { - var size = name2int(name); + var size = name2int(pname); var r = sr != null ? sr : JCAUtil.getDefSecureRandom(); byte[] rnd = new byte[32]; r.nextBytes(rnd); @@ -160,10 +220,10 @@ public class ML_DSA_Impls { } @Override - protected boolean implVerify(String name, byte[] pkBytes, + protected boolean implVerify(String pname, byte[] pkBytes, Object pk2, byte[] msg, byte[] sigBytes) throws SignatureException { - var size = name2int(name); + var size = name2int(pname); var mlDsa = new ML_DSA(size); if (version == Version.FINAL) { // FIPS 204 Algorithm 3 ML-DSA.Verify prepend {0, len(ctx)} @@ -176,18 +236,18 @@ public class ML_DSA_Impls { } @Override - protected Object implCheckPublicKey(String name, byte[] pk) + protected Object implCheckPublicKey(String pname, byte[] pk) throws InvalidKeyException { - ML_DSA mlDsa = new ML_DSA(name2int(name)); + ML_DSA mlDsa = new ML_DSA(name2int(pname)); return mlDsa.checkPublicKey(pk); } @Override - protected Object implCheckPrivateKey(String name, byte[] sk) + protected Object implCheckPrivateKey(String pname, byte[] sk) throws InvalidKeyException { - ML_DSA mlDsa = new ML_DSA(name2int(name)); + ML_DSA mlDsa = new ML_DSA(name2int(pname)); return mlDsa.checkPrivateKey(sk); } } diff --git a/src/java.base/share/classes/sun/security/provider/NamedKEM.java b/src/java.base/share/classes/sun/security/provider/NamedKEM.java index 2731b3460af..60449396d4d 100644 --- a/src/java.base/share/classes/sun/security/provider/NamedKEM.java +++ b/src/java.base/share/classes/sun/security/provider/NamedKEM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -42,7 +42,6 @@ import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.NamedParameterSpec; import java.util.Arrays; -import java.util.Objects; /// A base class for all `KEM` implementations that can be /// configured with a named parameter set. See [NamedKeyPairGenerator] @@ -50,21 +49,19 @@ import java.util.Objects; public abstract class NamedKEM implements KEMSpi { private final String fname; // family name - private final String[] pnames; // allowed parameter set name (at least one) + private final NamedKeyFactory fac; /// Creates a new `NamedKEM` object. /// /// @param fname the family name - /// @param pnames the standard parameter set names, at least one is needed. - protected NamedKEM(String fname, String... pnames) { + /// @param fac the `KeyFactory` used to translate foreign keys and + /// perform key validation + protected NamedKEM(String fname, NamedKeyFactory fac) { if (fname == null) { throw new AssertionError("fname cannot be null"); } - if (pnames == null || pnames.length == 0) { - throw new AssertionError("pnames cannot be null or empty"); - } this.fname = fname; - this.pnames = pnames; + this.fac = fac; } @Override @@ -76,8 +73,7 @@ public abstract class NamedKEM implements KEMSpi { "The " + fname + " algorithm does not take any parameters"); } // translate also check the key - var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames) - .engineTranslateKey(publicKey); + var nk = (NamedX509Key) fac.toNamedKey(publicKey); var pk = nk.getRawBytes(); return getKeyConsumerImpl(this, nk.getParams(), pk, implCheckPublicKey(nk.getParams().getName(), pk), secureRandom); @@ -92,16 +88,15 @@ public abstract class NamedKEM implements KEMSpi { "The " + fname + " algorithm does not take any parameters"); } // translate also check the key - var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames) - .engineTranslateKey(privateKey); - var sk = nk.getRawBytes(); + var nk = (NamedPKCS8Key) fac.toNamedKey(privateKey); + var sk = nk.getExpanded(); return getKeyConsumerImpl(this, nk.getParams(), sk, implCheckPrivateKey(nk.getParams().getName(), sk), null); } // We don't have a flag on whether key is public key or private key. // The correct method should always be called. - private record KeyConsumerImpl(NamedKEM kem, String name, int sslen, + private record KeyConsumerImpl(NamedKEM kem, String pname, int sslen, int clen, byte[] key, Object k2, SecureRandom sr) implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi { @Override @@ -110,7 +105,7 @@ public abstract class NamedKEM implements KEMSpi { if (encapsulation.length != clen) { throw new DecapsulateException("Invalid key encapsulation message length"); } - var ss = kem.implDecapsulate(name, key, k2, encapsulation); + var ss = kem.implDecapsulate(pname, key, k2, encapsulation); try { return new SecretKeySpec(ss, from, to - from, algorithm); @@ -121,7 +116,7 @@ public abstract class NamedKEM implements KEMSpi { @Override public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { - var enc = kem.implEncapsulate(name, key, k2, sr); + var enc = kem.implEncapsulate(pname, key, k2, sr); try { return new KEM.Encapsulated( new SecretKeySpec(enc[1], @@ -146,46 +141,46 @@ public abstract class NamedKEM implements KEMSpi { private static KeyConsumerImpl getKeyConsumerImpl(NamedKEM kem, NamedParameterSpec nps, byte[] key, Object k2, SecureRandom sr) { - String name = nps.getName(); - return new KeyConsumerImpl(kem, name, kem.implSecretSize(name), kem.implEncapsulationSize(name), + String pname = nps.getName(); + return new KeyConsumerImpl(kem, pname, kem.implSecretSize(pname), kem.implEncapsulationSize(pname), key, k2, sr); } /// User-defined encap function. /// - /// @param name parameter name + /// @param pname parameter name /// @param pk public key in raw bytes /// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey]. /// @param sr SecureRandom object, `null` if not initialized /// @return the key encapsulation message and the shared key (in this order) /// @throws ProviderException if there is an internal error - protected abstract byte[][] implEncapsulate(String name, byte[] pk, Object pk2, SecureRandom sr); + protected abstract byte[][] implEncapsulate(String pname, byte[] pk, Object pk2, SecureRandom sr); /// User-defined decap function. /// - /// @param name parameter name + /// @param pname parameter name /// @param sk private key in raw bytes /// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey]. /// @param encap the key encapsulation message /// @return the shared key /// @throws ProviderException if there is an internal error /// @throws DecapsulateException if there is another error - protected abstract byte[] implDecapsulate(String name, byte[] sk, Object sk2, byte[] encap) + protected abstract byte[] implDecapsulate(String pname, byte[] sk, Object sk2, byte[] encap) throws DecapsulateException; /// User-defined function returning shared secret key length. /// - /// @param name parameter name + /// @param pname parameter name /// @return shared secret key length /// @throws ProviderException if there is an internal error - protected abstract int implSecretSize(String name); + protected abstract int implSecretSize(String pname); /// User-defined function returning key encapsulation message length. /// - /// @param name parameter name + /// @param pname parameter name /// @return key encapsulation message length /// @throws ProviderException if there is an internal error - protected abstract int implEncapsulationSize(String name); + protected abstract int implEncapsulationSize(String pname); /// User-defined function to validate a public key. /// @@ -196,11 +191,11 @@ public abstract class NamedKEM implements KEMSpi { /// /// The default implementation returns `null`. /// - /// @param name parameter name + /// @param pname parameter name /// @param pk public key in raw bytes /// @return a parsed key, `null` if none. /// @throws InvalidKeyException if the key is invalid - protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException { + protected Object implCheckPublicKey(String pname, byte[] pk) throws InvalidKeyException { return null; } @@ -213,11 +208,11 @@ public abstract class NamedKEM implements KEMSpi { /// /// The default implementation returns `null`. /// - /// @param name parameter name + /// @param pname parameter name /// @param sk private key in raw bytes /// @return a parsed key, `null` if none. /// @throws InvalidKeyException if the key is invalid - protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException { + protected Object implCheckPrivateKey(String pname, byte[] sk) throws InvalidKeyException { return null; } } diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java index 727358dd074..9099f1446ff 100644 --- a/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java +++ b/src/java.base/share/classes/sun/security/provider/NamedKeyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -42,7 +42,6 @@ import java.security.spec.NamedParameterSpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; -import java.util.Objects; /// A base class for all `KeyFactory` implementations that can be /// configured with a named parameter set. See [NamedKeyPairGenerator] @@ -58,7 +57,7 @@ import java.util.Objects; /// /// When reading from a RAW format, it needs enough info to derive the /// parameter set name. -public class NamedKeyFactory extends KeyFactorySpi { +public abstract class NamedKeyFactory extends KeyFactorySpi { private final String fname; // family name private final String[] pnames; // allowed parameter set name (at least one) @@ -78,92 +77,110 @@ public class NamedKeyFactory extends KeyFactorySpi { this.pnames = pnames; } - private String checkName(String name) throws InvalidKeyException { - for (var pname : pnames) { - if (pname.equalsIgnoreCase(name)) { + private String checkName(String pname) throws InvalidKeyException { + for (var n : pnames) { + if (n.equalsIgnoreCase(pname)) { // return the stored standard name - return pname; + return n; } } - throw new InvalidKeyException("Unsupported parameter set name: " + name); + throw new InvalidKeyException("Unsupported parameter set name: " + pname); } @Override protected PublicKey engineGeneratePublic(KeySpec keySpec) throws InvalidKeySpecException { - if (keySpec instanceof X509EncodedKeySpec xspec) { - try { - return fromX509(xspec.getEncoded()); - } catch (InvalidKeyException e) { - throw new InvalidKeySpecException(e); + return switch (keySpec) { + case X509EncodedKeySpec xspec -> { + try { + yield fromX509(xspec.getEncoded()); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } } - } else if (keySpec instanceof RawKeySpec rks) { - if (pnames.length == 1) { - return new NamedX509Key(fname, pnames[0], rks.getKeyArr()); - } else { - throw new InvalidKeySpecException("Parameter set name unavailable"); + case RawKeySpec rks -> { + if (pnames.length == 1) { + yield new NamedX509Key(fname, pnames[0], rks.getKeyArr()); + } else { + throw new InvalidKeySpecException("Parameter set name unavailable"); + } } - } else if (keySpec instanceof EncodedKeySpec espec - && espec.getFormat().equalsIgnoreCase("RAW")) { - if (pnames.length == 1) { - return new NamedX509Key(fname, pnames[0], espec.getEncoded()); - } else { - throw new InvalidKeySpecException("Parameter set name unavailable"); + case EncodedKeySpec espec when espec.getFormat().equalsIgnoreCase("RAW") -> { + if (pnames.length == 1) { + yield new NamedX509Key(fname, pnames[0], espec.getEncoded()); + } else { + throw new InvalidKeySpecException("Parameter set name unavailable"); + } } - } else { - throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec); - } + case null -> throw new InvalidKeySpecException( + "keySpec must not be null"); + default -> + throw new InvalidKeySpecException(keySpec.getClass().getName() + + " not supported."); + }; } @Override protected PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { - if (keySpec instanceof PKCS8EncodedKeySpec pspec) { - var bytes = pspec.getEncoded(); - try { - return fromPKCS8(bytes); - } catch (InvalidKeyException e) { - throw new InvalidKeySpecException(e); - } finally { - Arrays.fill(bytes, (byte) 0); - } - } else if (keySpec instanceof RawKeySpec rks) { - if (pnames.length == 1) { - var bytes = rks.getKeyArr(); + return switch (keySpec) { + case PKCS8EncodedKeySpec pspec -> { + var bytes = pspec.getEncoded(); try { - return new NamedPKCS8Key(fname, pnames[0], bytes); + yield fromPKCS8(bytes); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); } finally { Arrays.fill(bytes, (byte) 0); } - } else { - throw new InvalidKeySpecException("Parameter set name unavailable"); } - } else if (keySpec instanceof EncodedKeySpec espec - && espec.getFormat().equalsIgnoreCase("RAW")) { - if (pnames.length == 1) { - var bytes = espec.getEncoded(); - try { - return new NamedPKCS8Key(fname, pnames[0], bytes); - } finally { - Arrays.fill(bytes, (byte) 0); + case RawKeySpec rks -> { + if (pnames.length == 1) { + var raw = rks.getKeyArr(); + try { + yield fromRaw(pnames[0], raw); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException("Invalid key input", e); + } + } else { + throw new InvalidKeySpecException("Parameter set name unavailable"); } - } else { - throw new InvalidKeySpecException("Parameter set name unavailable"); } - } else { - throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec); - } + case EncodedKeySpec espec when espec.getFormat().equalsIgnoreCase("RAW") -> { + if (pnames.length == 1) { + var raw = espec.getEncoded(); + try { + yield fromRaw(pnames[0], raw); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException("Invalid key input", e); + } + } else { + throw new InvalidKeySpecException("Parameter set name unavailable"); + } + } + case null -> throw new InvalidKeySpecException( + "keySpec must not be null"); + default -> + throw new InvalidKeySpecException(keySpec.getClass().getName() + + " not supported."); + }; + } + + private PrivateKey fromRaw(String pname, byte[] raw) + throws InvalidKeyException { + return NamedPKCS8Key.internalCreate( + fname, pname, raw, implExpand(pname, raw)); } private PrivateKey fromPKCS8(byte[] bytes) - throws InvalidKeyException, InvalidKeySpecException { - var k = new NamedPKCS8Key(fname, bytes); + throws InvalidKeyException { + var k = new NamedPKCS8Key(fname, bytes, this::implExpand); checkName(k.getParams().getName()); return k; } private PublicKey fromX509(byte[] bytes) - throws InvalidKeyException, InvalidKeySpecException { + throws InvalidKeyException { var k = new NamedX509Key(fname, bytes); checkName(k.getParams().getName()); return k; @@ -184,7 +201,7 @@ public class NamedKeyFactory extends KeyFactorySpi { protected T engineGetKeySpec(Key key, Class keySpec) throws InvalidKeySpecException { try { - key = engineTranslateKey(key); + key = toNamedKey(key); } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); } @@ -225,6 +242,12 @@ public class NamedKeyFactory extends KeyFactorySpi { @Override protected Key engineTranslateKey(Key key) throws InvalidKeyException { + // The base toNamedKey only makes sure key is translated into a NamedKey. + // the key material is still the same as the input. + return toNamedKey(key); + } + + protected Key toNamedKey(Key key) throws InvalidKeyException { if (key == null) { throw new InvalidKeyException("Key must not be null"); } @@ -242,27 +265,28 @@ public class NamedKeyFactory extends KeyFactorySpi { } else if (format.equalsIgnoreCase("RAW")) { var kAlg = key.getAlgorithm(); if (key instanceof AsymmetricKey pk) { - String name; + String pname; // Three cases that we can find the parameter set name from a RAW key: // 1. getParams() returns one // 2. getAlgorithm() returns param set name (some provider does this) // 3. getAlgorithm() returns family name but this KF is for param set name if (pk.getParams() instanceof NamedParameterSpec nps) { - name = checkName(nps.getName()); + pname = checkName(nps.getName()); } else { if (kAlg.equalsIgnoreCase(fname)) { if (pnames.length == 1) { - name = pnames[0]; + pname = pnames[0]; } else { throw new InvalidKeyException("No parameter set info"); } } else { - name = checkName(kAlg); + pname = checkName(kAlg); } } + var raw = key.getEncoded(); return key instanceof PrivateKey - ? new NamedPKCS8Key(fname, name, key.getEncoded()) - : new NamedX509Key(fname, name, key.getEncoded()); + ? fromRaw(pname, raw) + : new NamedX509Key(fname, pname, raw); } else { throw new InvalidKeyException("Unsupported key type: " + key.getClass()); } @@ -270,19 +294,26 @@ public class NamedKeyFactory extends KeyFactorySpi { var bytes = key.getEncoded(); try { return fromPKCS8(bytes); - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException("Invalid PKCS#8 key", e); } finally { Arrays.fill(bytes, (byte) 0); } } else if (format.equalsIgnoreCase("X.509") && key instanceof PublicKey) { - try { - return fromX509(key.getEncoded()); - } catch (InvalidKeySpecException e) { - throw new InvalidKeyException("Invalid X.509 key", e); - } + return fromX509(key.getEncoded()); } else { throw new InvalidKeyException("Unsupported key format: " + key.getFormat()); } } + + /// User-defined function to generate the expanded format of + /// a [NamedPKCS8Key] from its encoding format. + /// + /// This method is called when the key factory is constructing a private + /// key. The ownership of the result is fully granted to the caller. + /// + /// @param pname the parameter set name + /// @param input the encoding, could be any format + /// @return the expanded key, not null + /// @throws InvalidKeyException if `input` is invalid + protected abstract byte[] implExpand(String pname, byte[] input) + throws InvalidKeyException; } diff --git a/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java b/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java index 5be2b2b2a08..6b55924b0fe 100644 --- a/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java +++ b/src/java.base/share/classes/sun/security/provider/NamedKeyPairGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -36,7 +36,6 @@ import java.security.ProviderException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.NamedParameterSpec; -import java.util.Objects; /// A base class for all `KeyPairGenerator` implementations that can be /// configured with a named parameter set. @@ -52,15 +51,21 @@ import java.util.Objects; /// with `getAlgorithm` returning the family name, and `getParams` returning /// the parameter set name as a [NamedParameterSpec] object. /// -/// An implementation must include a zero-argument public constructor that -/// calls `super(fname, pnames)`, where `fname` is the family name of the -/// algorithm and `pnames` are its supported parameter set names. `pnames` -/// must contain at least one element. For an implementation of -/// `NamedKeyPairGenerator`, the first element becomes its default parameter -/// set, i.e. the parameter set to be used in key pair generation unless +/// A `NamedKeyPairGenerator` or `NamedKeyFactory` implementation must include +/// a zero-argument public constructor that calls `super(fname, pnames)`, where +/// `fname` is the family name of the algorithm and `pnames` are its supported +/// parameter set names. `pnames` must contain at least one element. For an +/// implementation of `NamedKeyPairGenerator`, the first element becomes its +/// default parameter set, i.e. the parameter set used by generated keys unless /// [#initialize(AlgorithmParameterSpec, java.security.SecureRandom)] /// is called on a different parameter set. /// +/// A `NamedKEM` or `NamedSignature` implementation must include a zero-argument +/// public constructor that calls `super(fname, factory)`, where `fname` is the +/// family name of the algorithm and `factory` is the `NamedKeyFactory` object +/// that is used to translate foreign keys. `factory` only recognizes +/// parameter sets supported by this implementation. +/// /// An implementation must implement all abstract methods. For all these /// methods, the implementation must relinquish any "ownership" of any input /// and output array argument. Precisely, the implementation must not retain @@ -69,8 +74,8 @@ import java.util.Objects; /// array argument and must not retain any reference to an input array argument /// after the call. /// -/// Also, an implementation must not keep any extra copy of a private key. -/// For key generation, the only copy is the one returned in the +/// Also, an implementation must not keep any extra copy of a private key in +/// any format. For key generation, the only copy is the one returned in the /// [#implGenerateKeyPair] call. For all other methods, it must not make /// a copy of the input private key. A `KEM` implementation also must not /// keep a copy of the shared secret key, no matter if it's an encapsulator @@ -84,6 +89,34 @@ import java.util.Objects; /// (For example, `implSign`) later. An implementation must not retain /// a reference of the parsed key. /// +/// The private key, represented as a byte array when used in `NamedKEM` or +/// `NamedSignature`, is referred to as its expanded format. For some +/// algorithms, this format may differ from the +/// [key material][NamedPKCS8Key#getRawBytes()] inside a PKCS #8 file. For example, +/// [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) +/// Table 2 defines the ML-DSA-65 private key as a 4032-byte array, which is +/// used in the ML-DSA.Sign function in Algorithm 2, representing the +/// expanded format. However, in +/// [RFC 9881](https://datatracker.ietf.org/doc/html/rfc9881#name-private-key-format), +/// a private key can be encoded into a CHOICE of three formats, none in the +/// same as the FIPS 204 format. The choices are defined in +/// [sun.security.util.KeyChoices]. A `NamedKeyPairGenerator` implementation +/// should return both the expanded key and a preferred encoding in its +/// [#implGenerateKeyPair] method. +/// +/// A `NamedKeyFactory` must override the `implExpand` method to derive +/// the expanded format from an encoding format, or return `null` if there +/// is no difference. +/// +/// Implementations may support multiple encoding formats. +/// +/// A `NamedKeyFactory` must not modify the encoding when generating a key +/// from a `KeySpec` object, ensuring that when re-encoded, the key retains +/// its original encoding format. +/// +/// A `NamedKeyFactory` can choose a different encoding format when +/// `translateKey` is called. +/// /// When constructing a [NamedX509Key] or [NamedPKCS8Key] object from raw key /// bytes, the key bytes are directly referenced within the object, so the /// caller must not modify them afterward. Similarly, the key's `getRawBytes` @@ -105,9 +138,9 @@ import java.util.Objects; public abstract class NamedKeyPairGenerator extends KeyPairGeneratorSpi { private final String fname; // family name - private final String[] pnames; // allowed parameter set name (at least one) + private final String[] pnames; // allowed parameter set names (at least one) - protected String name; // init as + protected String pname; // parameter set name, if can be determined private SecureRandom secureRandom; /// Creates a new `NamedKeyPairGenerator` object. @@ -126,22 +159,22 @@ public abstract class NamedKeyPairGenerator extends KeyPairGeneratorSpi { this.pnames = pnames; } - private String checkName(String name) throws InvalidAlgorithmParameterException { - for (var pname : pnames) { - if (pname.equalsIgnoreCase(name)) { - // return the stored standard name - return pname; + private String checkName(String pname) throws InvalidAlgorithmParameterException { + for (var n : pnames) { + if (n.equalsIgnoreCase(pname)) { + // return the stored standard pname + return n; } } throw new InvalidAlgorithmParameterException( - "Unsupported parameter set name: " + name); + "Unsupported parameter set name: " + pname); } @Override public void initialize(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { if (params instanceof NamedParameterSpec spec) { - name = checkName(spec.getName()); + pname = checkName(spec.getName()); } else { throw new InvalidAlgorithmParameterException( "Unsupported AlgorithmParameterSpec: " + params); @@ -161,17 +194,21 @@ public abstract class NamedKeyPairGenerator extends KeyPairGeneratorSpi { @Override public KeyPair generateKeyPair() { - String pname = name != null ? name : pnames[0]; - var keys = implGenerateKeyPair(pname, secureRandom); - return new KeyPair(new NamedX509Key(fname, pname, keys[0]), - new NamedPKCS8Key(fname, pname, keys[1])); + String tmpName = pname != null ? pname : pnames[0]; + var keys = implGenerateKeyPair(tmpName, secureRandom); + return new KeyPair(new NamedX509Key(fname, tmpName, keys[0]), + NamedPKCS8Key.internalCreate(fname, tmpName, keys[1], + keys.length == 2 ? null : keys[2])); } /// User-defined key pair generator. /// /// @param pname parameter set name /// @param sr `SecureRandom` object, `null` if not initialized - /// @return public key and private key (in this order) in raw bytes + /// @return the public key, the private key in its encoding format, and + /// the private key in its expanded format (in this order) in + /// raw bytes. If the expanded format of the private key is the + /// same as its encoding format, the 3rd element must be omitted. /// @throws ProviderException if there is an internal error protected abstract byte[][] implGenerateKeyPair(String pname, SecureRandom sr); } diff --git a/src/java.base/share/classes/sun/security/provider/NamedSignature.java b/src/java.base/share/classes/sun/security/provider/NamedSignature.java index 921a39cfc92..07d20828c3c 100644 --- a/src/java.base/share/classes/sun/security/provider/NamedSignature.java +++ b/src/java.base/share/classes/sun/security/provider/NamedSignature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -40,7 +40,6 @@ import java.security.SecureRandom; import java.security.SignatureException; import java.security.SignatureSpi; import java.security.spec.AlgorithmParameterSpec; -import java.util.Objects; /// A base class for all `Signature` implementations that can be /// configured with a named parameter set. See [NamedKeyPairGenerator] @@ -50,12 +49,12 @@ import java.util.Objects; public abstract class NamedSignature extends SignatureSpi { private final String fname; // family name - private final String[] pnames; // allowed parameter set name (at least one) + private final NamedKeyFactory fac; private final ByteArrayOutputStream bout = new ByteArrayOutputStream(); // init with... - private String name; + private String pname; private byte[] secKey; private byte[] pubKey; @@ -65,26 +64,23 @@ public abstract class NamedSignature extends SignatureSpi { /// Creates a new `NamedSignature` object. /// /// @param fname the family name - /// @param pnames the standard parameter set names, at least one is needed. - protected NamedSignature(String fname, String... pnames) { + /// @param fac the `KeyFactory` used to translate foreign keys and + /// perform key validation + protected NamedSignature(String fname, NamedKeyFactory fac) { if (fname == null) { throw new AssertionError("fname cannot be null"); } - if (pnames == null || pnames.length == 0) { - throw new AssertionError("pnames cannot be null or empty"); - } this.fname = fname; - this.pnames = pnames; + this.fac = fac; } @Override protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { // translate also check the key - var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames) - .engineTranslateKey(publicKey); - name = nk.getParams().getName(); + var nk = (NamedX509Key) fac.toNamedKey(publicKey); + pname = nk.getParams().getName(); pubKey = nk.getRawBytes(); - pk2 = implCheckPublicKey(name, pubKey); + pk2 = implCheckPublicKey(pname, pubKey); secKey = null; bout.reset(); } @@ -92,11 +88,10 @@ public abstract class NamedSignature extends SignatureSpi { @Override protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { // translate also check the key - var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames) - .engineTranslateKey(privateKey); - name = nk.getParams().getName(); - secKey = nk.getRawBytes(); - sk2 = implCheckPrivateKey(name, secKey); + var nk = (NamedPKCS8Key) fac.toNamedKey(privateKey); + pname = nk.getParams().getName(); + secKey = nk.getExpanded(); + sk2 = implCheckPrivateKey(pname, secKey); pubKey = null; bout.reset(); } @@ -116,7 +111,7 @@ public abstract class NamedSignature extends SignatureSpi { if (secKey != null) { var msg = bout.toByteArray(); bout.reset(); - return implSign(name, secKey, sk2, msg, appRandom); + return implSign(pname, secKey, sk2, msg, appRandom); } else { throw new SignatureException("No private key"); } @@ -127,21 +122,21 @@ public abstract class NamedSignature extends SignatureSpi { if (pubKey != null) { var msg = bout.toByteArray(); bout.reset(); - return implVerify(name, pubKey, pk2, msg, sig); + return implVerify(pname, pubKey, pk2, msg, sig); } else { throw new SignatureException("No public key"); } } @Override - @SuppressWarnings("deprecation") + @Deprecated protected void engineSetParameter(String param, Object value) throws InvalidParameterException { throw new InvalidParameterException("setParameter() not supported"); } @Override - @SuppressWarnings("deprecation") + @Deprecated protected Object engineGetParameter(String param) throws InvalidParameterException { throw new InvalidParameterException("getParameter() not supported"); } @@ -162,7 +157,7 @@ public abstract class NamedSignature extends SignatureSpi { /// User-defined sign function. /// - /// @param name parameter name + /// @param pname parameter name /// @param sk private key in raw bytes /// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey]. /// @param msg the message @@ -170,12 +165,12 @@ public abstract class NamedSignature extends SignatureSpi { /// @return the signature /// @throws ProviderException if there is an internal error /// @throws SignatureException if there is another error - protected abstract byte[] implSign(String name, byte[] sk, Object sk2, + protected abstract byte[] implSign(String pname, byte[] sk, Object sk2, byte[] msg, SecureRandom sr) throws SignatureException; /// User-defined verify function. /// - /// @param name parameter name + /// @param pname parameter name /// @param pk public key in raw bytes /// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey]. /// @param msg the message @@ -183,7 +178,7 @@ public abstract class NamedSignature extends SignatureSpi { /// @return true if verified /// @throws ProviderException if there is an internal error /// @throws SignatureException if there is another error - protected abstract boolean implVerify(String name, byte[] pk, Object pk2, + protected abstract boolean implVerify(String pname, byte[] pk, Object pk2, byte[] msg, byte[] sig) throws SignatureException; /// User-defined function to validate a public key. @@ -195,11 +190,11 @@ public abstract class NamedSignature extends SignatureSpi { /// /// The default implementation returns `null`. /// - /// @param name parameter name + /// @param pname parameter name /// @param pk public key in raw bytes /// @return a parsed key, `null` if none. /// @throws InvalidKeyException if the key is invalid - protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException { + protected Object implCheckPublicKey(String pname, byte[] pk) throws InvalidKeyException { return null; } @@ -212,11 +207,11 @@ public abstract class NamedSignature extends SignatureSpi { /// /// The default implementation returns `null`. /// - /// @param name parameter name + /// @param pname parameter name /// @param sk private key in raw bytes /// @return a parsed key, `null` if none. /// @throws InvalidKeyException if the key is invalid - protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException { + protected Object implCheckPrivateKey(String pname, byte[] sk) throws InvalidKeyException { return null; } } diff --git a/src/java.base/share/classes/sun/security/util/KeyChoices.java b/src/java.base/share/classes/sun/security/util/KeyChoices.java new file mode 100644 index 00000000000..da3c611750e --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/KeyChoices.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 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. + */ + +package sun.security.util; + +import java.security.*; +import java.util.Arrays; +import java.util.Locale; +import java.util.function.BiFunction; + +/** + * The content of an ML-KEM or ML-DSA private key is defined as a CHOICE + * among three different representations. For example: + *

    + *  ML-KEM-1024-PrivateKey ::= CHOICE {
    + *       seed [0] OCTET STRING (SIZE (64)),
    + *       expandedKey OCTET STRING (SIZE (3168)),
    + *       both SEQUENCE {
    + *           seed OCTET STRING (SIZE (64)),
    + *           expandedKey OCTET STRING (SIZE (3168))
    + *           }
    + *       }
    + * 
    + * This class supports reading, writing, and converting between them. + *

    + * Current code follows draft-ietf-lamps-kyber-certificates-11 and RFC 9881. + */ +public final class KeyChoices { + + public enum Type { SEED, EXPANDED_KEY, BOTH } + + private record Choice(Type type, byte[] seed, byte[] expanded) {} + + /** + * Gets the preferred choice type for an algorithm, defined as an + * overridable security property "jdk..pkcs8.encoding". + * + * @param name "mlkem" or "mldsa". + * @throws IllegalArgumentException if property is invalid value + * @return the type + */ + public static Type getPreferred(String name) { + var prop = SecurityProperties.getOverridableProperty( + "jdk." + name + ".pkcs8.encoding"); + if (prop == null) { + return Type.SEED; + } + return switch (prop.toLowerCase(Locale.ROOT)) { + case "seed" -> Type.SEED; + case "expandedkey" -> Type.EXPANDED_KEY; + case "both" -> Type.BOTH; + default -> throw new IllegalArgumentException("Unknown format: " + prop); + }; + } + + /** + * Writes one of the ML-KEM or ML-DSA private key formats. + *

    + * This method does not check the length of the inputs or whether + * they match each other. The caller must make sure `seed` and/or + * `expanded` are provided if `type` requires any of them. + * + * @param type preferred output choice type + * @param seed the seed, could be null + * @param expanded the expanded key, could be null + * @return one of the choices + */ + public static byte[] writeToChoice(Type type, byte[] seed, byte[] expanded) { + byte[] skOctets; + // Ensures using one-byte len in DER + assert seed == null || seed.length < 128; + // Ensures using two-byte len in DER + assert expanded == null || expanded.length > 256 && expanded.length < 60000; + + return switch (type) { + case SEED -> { + assert seed != null; + skOctets = new byte[seed.length + 2]; + skOctets[0] = (byte)0x80; + skOctets[1] = (byte) seed.length; + System.arraycopy(seed, 0, skOctets, 2, seed.length); + yield skOctets; + } + case EXPANDED_KEY -> { + assert expanded != null; + skOctets = new byte[expanded.length + 4]; + skOctets[0] = 0x04; + writeShortLength(skOctets, 1, expanded.length); + System.arraycopy(expanded, 0, skOctets, 4, expanded.length); + yield skOctets; + } + case BOTH -> { + assert seed != null; + assert expanded != null; + skOctets = new byte[10 + seed.length + expanded.length]; + skOctets[0] = 0x30; + writeShortLength(skOctets, 1, 6 + seed.length + expanded.length); + skOctets[4] = 0x04; + skOctets[5] = (byte)seed.length; + System.arraycopy(seed, 0, skOctets, 6, seed.length); + skOctets[6 + seed.length] = 0x04; + writeShortLength(skOctets, 7 + seed.length, expanded.length); + System.arraycopy(expanded, 0, skOctets, 10 + seed.length, expanded.length); + yield skOctets; + } + }; + } + + /** + * Gets the type of input. + * + * @param input input bytes + * @return the type + * @throws InvalidKeyException if input is invalid + */ + public static Type typeOfChoice(byte[] input) throws InvalidKeyException { + if (input.length < 1) { + throw new InvalidKeyException("Empty key"); + } + return switch (input[0]) { + case (byte) 0x80 -> Type.SEED; + case 0x04 -> Type.EXPANDED_KEY; + case 0x30 -> Type.BOTH; + default -> throw new InvalidKeyException("Wrong tag: " + input[0]); + }; + } + + /** + * Splits one of the ML-KEM or ML-DSA private key formats into + * seed and expandedKey, if exists. + * + * @param seedLen correct seed length + * @param input input bytes + * @return a {@code Choice} object. Byte arrays inside are newly allocated + * @throws InvalidKeyException if input is invalid + */ + private static Choice readFromChoice(int seedLen, byte[] input) + throws InvalidKeyException { + if (input.length < seedLen + 2) { + throw new InvalidKeyException("Too short"); + } + return switch (input[0]) { + case (byte) 0x80 -> { + // 80 SEED_LEN + if (input[1] != seedLen && input.length != seedLen + 2) { + throw new InvalidKeyException("Invalid seed"); + } + yield new Choice(Type.SEED, + Arrays.copyOfRange(input, 2, seedLen + 2), null); + } + case 0x04 -> { + // 04 82 nn nn + if (readShortLength(input, 1) != input.length - 4) { + throw new InvalidKeyException("Invalid expandedKey"); + } + yield new Choice(Type.EXPANDED_KEY, + null, Arrays.copyOfRange(input, 4, input.length)); + } + case 0x30 -> { + // 30 82 mm mm 04 SEED_LEN 04 82 nn nn + if (input.length < 6 + seedLen + 4) { + throw new InvalidKeyException("Too short"); + } + if (readShortLength(input, 1) != input.length - 4 + || input[4] != 0x04 + || input[5] != (byte)seedLen + || input[seedLen + 6] != 0x04 + || readShortLength(input, seedLen + 7) + != input.length - 10 - seedLen) { + throw new InvalidKeyException("Invalid both"); + } + yield new Choice(Type.BOTH, + Arrays.copyOfRange(input, 6, 6 + seedLen), + Arrays.copyOfRange(input, seedLen + 10, input.length)); + } + default -> throw new InvalidKeyException("Wrong tag: " + input[0]); + }; + } + + /** + * Reads from any encoding and write to the specified type. + * + * @param type preferred output choice type + * @param pname parameter set name + * @param seedLen seed length + * @param input the input encoding + * @param expander function to calculate expanded from seed, could be null + * if there is already expanded in input + * @return the preferred encoding + * @throws InvalidKeyException if input is invalid or does not have enough + * information to generate the output + */ + public static byte[] choiceToChoice(Type type, String pname, + int seedLen, byte[] input, + BiFunction expander) + throws InvalidKeyException { + var choice = readFromChoice(seedLen, input); + try { + if (type != Type.EXPANDED_KEY && choice.type == Type.EXPANDED_KEY) { + throw new InvalidKeyException( + "key contains not enough info to translate"); + } + var expanded = (choice.expanded == null && type != Type.SEED) + ? expander.apply(pname, choice.seed) + : choice.expanded; + return writeToChoice(type, choice.seed, expanded); + } finally { + if (choice.seed != null) { + Arrays.fill(choice.seed, (byte) 0); + } + if (choice.expanded != null) { + Arrays.fill(choice.expanded, (byte) 0); + } + } + } + + /** + * Reads from any choice of encoding and return the expanded format. + * + * @param pname parameter set name + * @param seedLen seed length + * @param input input encoding + * @param expander function to calculate expanded from seed, could be null + * if there is already expanded in input + * @return the expanded key + * @throws InvalidKeyException if input is invalid + */ + public static byte[] choiceToExpanded(String pname, + int seedLen, byte[] input, + BiFunction expander) + throws InvalidKeyException { + var choice = readFromChoice(seedLen, input); + if (choice.type == Type.BOTH) { + var calculated = expander.apply(pname, choice.seed); + if (!Arrays.equals(choice.expanded, calculated)) { + throw new InvalidKeyException("seed and expandedKey do not match"); + } + Arrays.fill(calculated, (byte)0); + } + try { + if (choice.expanded != null) { + return choice.expanded; + } + return expander.apply(pname, choice.seed); + } finally { + if (choice.seed != null) { + Arrays.fill(choice.seed, (byte)0); + } + } + } + + // Reads a 2 bytes length from DER encoding + private static int readShortLength(byte[] input, int from) + throws InvalidKeyException { + if (input[from] != (byte)0x82) { + throw new InvalidKeyException("Unexpected length"); + } + return ((input[from + 1] & 0xff) << 8) + (input[from + 2] & 0xff); + } + + // Writes a 2 bytes length to DER encoding + private static void writeShortLength(byte[] input, int from, int value) { + input[from] = (byte)0x82; + input[from + 1] = (byte) (value >> 8); + input[from + 2] = (byte) (value); + } +} diff --git a/src/java.base/share/classes/sun/security/x509/NamedX509Key.java b/src/java.base/share/classes/sun/security/x509/NamedX509Key.java index dc36bd3b9b3..0c3fe2bf121 100644 --- a/src/java.base/share/classes/sun/security/x509/NamedX509Key.java +++ b/src/java.base/share/classes/sun/security/x509/NamedX509Key.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -71,7 +71,8 @@ public final class NamedX509Key extends X509Key { setKey(new BitArray(rawBytes.length * 8, rawBytes)); } - /// Ctor from family name, and X.509 bytes + /// Ctor from family name, and X.509 bytes. Input byte array + /// is copied. Caller can modify it after the method call. public NamedX509Key(String fname, byte[] encoded) throws InvalidKeyException { this.fname = fname; decode(encoded); diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 9a81ba86268..ef4d7285f51 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -1700,3 +1700,28 @@ jdk.epkcs8.defaultAlgorithm=PBEWithHmacSHA256AndAES_128 # com.sun.security.allowedAIALocations=http://some.company.com/cacert \ # ldap://ldap.company.com/dc=company,dc=com?caCertificate;binary com.sun.security.allowedAIALocations= + +# +# PKCS #8 encoding format for newly created ML-KEM and ML-DSA private keys +# +# draft-ietf-lamps-kyber-certificates-11 and RFC 9881 define three possible formats for a private key: +# a seed (64 bytes for ML-KEM, 32 bytes for ML-DSA), an expanded private key, +# or a sequence containing both. +# +# The following security properties determine the encoding format used when a +# new keypair is generated with a KeyPairGenerator, and the output of the +# translateKey method on an existing key using a ML-KEM or ML-DSA KeyFactory. +# +# Valid values for these properties are "seed", "expandedKey", and "both" +# (case-insensitive). The default is "seed". +# +# If a system property of the same name is also specified, it supersedes the +# security property value defined here. +# +# Note: These properties are currently used by the SunJCE (for ML-KEM) and SUN +# (for ML-DSA) providers in the JDK Reference implementation. They are not +# guaranteed to be supported by other implementations or third-party security +# providers. +# +#jdk.mlkem.pkcs8.encoding = seed +#jdk.mldsa.pkcs8.encoding = seed diff --git a/test/jdk/sun/security/provider/acvp/Launcher.java b/test/jdk/sun/security/provider/acvp/Launcher.java index 680d1026275..2ee0ff96bd1 100644 --- a/test/jdk/sun/security/provider/acvp/Launcher.java +++ b/test/jdk/sun/security/provider/acvp/Launcher.java @@ -41,6 +41,8 @@ import java.util.zip.ZipFile; * ML_DSA_Impls.version might be modified * @library /test/lib * @modules java.base/sun.security.provider + * java.base/sun.security.util + * java.base/com.sun.crypto.provider * @run main/othervm/timeout=480 Launcher */ @@ -50,6 +52,8 @@ import java.util.zip.ZipFile; * @summary Test verifying the intrinsic implementation. * @library /test/lib * @modules java.base/sun.security.provider + * java.base/sun.security.util + * java.base/com.sun.crypto.provider * @run main/othervm/timeout=480 -Xcomp Launcher */ diff --git a/test/jdk/sun/security/provider/acvp/ML_DSA_Test.java b/test/jdk/sun/security/provider/acvp/ML_DSA_Test.java index 281bb415305..ac56642b8d7 100644 --- a/test/jdk/sun/security/provider/acvp/ML_DSA_Test.java +++ b/test/jdk/sun/security/provider/acvp/ML_DSA_Test.java @@ -24,6 +24,7 @@ import jdk.test.lib.Asserts; import jdk.test.lib.json.JSONValue; import jdk.test.lib.security.FixedSecureRandom; import sun.security.provider.ML_DSA_Impls; +import sun.security.util.DerOutputStream; import java.security.*; import java.security.spec.EncodedKeySpec; @@ -68,12 +69,13 @@ public class ML_DSA_Test { System.out.println(">> " + pname); for (var c : t.get("tests").asArray()) { System.out.print(c.get("tcId").asString() + " "); - g.initialize(np, new FixedSecureRandom(toByteArray(c.get("seed").asString()))); + var seed = toByteArray(c.get("seed").asString()); + g.initialize(np, new FixedSecureRandom(seed)); var kp = g.generateKeyPair(); var pk = f.getKeySpec(kp.getPublic(), EncodedKeySpec.class).getEncoded(); - var sk = f.getKeySpec(kp.getPrivate(), EncodedKeySpec.class).getEncoded(); Asserts.assertEqualsByteArray(toByteArray(c.get("pk").asString()), pk); - Asserts.assertEqualsByteArray(toByteArray(c.get("sk").asString()), sk); + Asserts.assertEqualsByteArray(toByteArray(c.get("sk").asString()), + ML_DSA_Impls.seedToExpanded(pname, seed)); } System.out.println(); } @@ -106,7 +108,7 @@ public class ML_DSA_Test { var sk = new PrivateKey() { public String getAlgorithm() { return pname; } public String getFormat() { return "RAW"; } - public byte[] getEncoded() { return toByteArray(c.get("sk").asString()); } + public byte[] getEncoded() { return oct(toByteArray(c.get("sk").asString())); } }; var sr = new FixedSecureRandom( det ? new byte[32] : toByteArray(c.get("rnd").asString())); @@ -119,6 +121,10 @@ public class ML_DSA_Test { } } + static byte[] oct(byte[] in) { + return new DerOutputStream().putOctetString(in).toByteArray(); + } + static void sigVerTest(JSONValue kat, Provider p) throws Exception { var s = p == null ? Signature.getInstance("ML-DSA") diff --git a/test/jdk/sun/security/provider/acvp/ML_KEM_Test.java b/test/jdk/sun/security/provider/acvp/ML_KEM_Test.java index c46c6a99e6d..35c1ce611da 100644 --- a/test/jdk/sun/security/provider/acvp/ML_KEM_Test.java +++ b/test/jdk/sun/security/provider/acvp/ML_KEM_Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -20,9 +20,11 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +import com.sun.crypto.provider.ML_KEM_Impls; import jdk.test.lib.Asserts; import jdk.test.lib.json.JSONValue; import jdk.test.lib.security.FixedSecureRandom; +import sun.security.util.DerOutputStream; import javax.crypto.KEM; import java.security.*; @@ -65,13 +67,14 @@ public class ML_KEM_Test { System.out.println(">> " + pname); for (var c : t.get("tests").asArray()) { System.out.print(c.get("tcId").asString() + " "); - g.initialize(np, new FixedSecureRandom( - toByteArray(c.get("d").asString()), toByteArray(c.get("z").asString()))); + var seed = toByteArray(c.get("d").asString() + c.get("z").asString()); + g.initialize(np, new FixedSecureRandom(seed)); var kp = g.generateKeyPair(); var pk = f.getKeySpec(kp.getPublic(), EncodedKeySpec.class).getEncoded(); - var sk = f.getKeySpec(kp.getPrivate(), EncodedKeySpec.class).getEncoded(); Asserts.assertEqualsByteArray(toByteArray(c.get("ek").asString()), pk); - Asserts.assertEqualsByteArray(toByteArray(c.get("dk").asString()), sk); + Asserts.assertEqualsByteArray( + toByteArray(c.get("dk").asString()), + ML_KEM_Impls.seedToExpanded(pname, seed)); } System.out.println(); } @@ -106,7 +109,7 @@ public class ML_KEM_Test { var dk = new PrivateKey() { public String getAlgorithm() { return pname; } public String getFormat() { return "RAW"; } - public byte[] getEncoded() { return toByteArray(t.get("dk").asString()); } + public byte[] getEncoded() { return oct(toByteArray(t.get("dk").asString())); } }; for (var c : t.get("tests").asArray()) { System.out.print(c.get("tcId").asString() + " "); @@ -118,4 +121,8 @@ public class ML_KEM_Test { } } } + + static byte[] oct(byte[] in) { + return new DerOutputStream().putOctetString(in).toByteArray(); + } } diff --git a/test/jdk/sun/security/provider/NamedEdDSA.java b/test/jdk/sun/security/provider/named/NamedEdDSA.java similarity index 84% rename from test/jdk/sun/security/provider/NamedEdDSA.java rename to test/jdk/sun/security/provider/named/NamedEdDSA.java index 4d0e3e9228a..30df8b22b31 100644 --- a/test/jdk/sun/security/provider/NamedEdDSA.java +++ b/test/jdk/sun/security/provider/named/NamedEdDSA.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 @@ -23,11 +23,12 @@ /* * @test - * @bug 8340327 + * @bug 8340327 8347938 * @modules java.base/sun.security.ec.ed * java.base/sun.security.ec.point * java.base/sun.security.jca * java.base/sun.security.provider + * java.base/sun.security.util * @library /test/lib */ @@ -40,7 +41,10 @@ import sun.security.jca.JCAUtil; import sun.security.provider.NamedKeyFactory; import sun.security.provider.NamedKeyPairGenerator; import sun.security.provider.NamedSignature; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import java.io.IOException; import java.security.*; import java.security.spec.EdDSAParameterSpec; import java.security.spec.NamedParameterSpec; @@ -66,11 +70,11 @@ public class NamedEdDSA { public static class EdDSASignature extends NamedSignature { public EdDSASignature() { - super("EdDSA", "Ed25519", "Ed448"); + super("EdDSA", new EdDSAKeyFactory()); } protected EdDSASignature(String pname) { - super("EdDSA", pname); + super("EdDSA", new EdDSAKeyFactory(pname)); } public static class Ed25519 extends EdDSASignature { @@ -86,22 +90,32 @@ public class NamedEdDSA { } @Override - public byte[] implSign(String name, byte[] sk, Object sk2, byte[] msg, SecureRandom sr) throws SignatureException { - return getOps(name).sign(plain, sk, msg); + public byte[] implSign(String pname, byte[] sk, Object sk2, byte[] msg, SecureRandom sr) { + return getOps(pname).sign(plain, sk, msg); } @Override - public boolean implVerify(String name, byte[] pk, Object pk2, byte[] msg, byte[] sig) throws SignatureException { - return getOps(name).verify(plain, (AffinePoint) pk2, pk, msg, sig); + public boolean implVerify(String pname, byte[] pk, Object pk2, byte[] msg, byte[] sig) throws SignatureException { + return getOps(pname).verify(plain, (AffinePoint) pk2, pk, msg, sig); } @Override - public Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException { - return getOps(name).decodeAffinePoint(InvalidKeyException::new, pk); + public Object implCheckPublicKey(String pname, byte[] pk) throws InvalidKeyException { + return getOps(pname).decodeAffinePoint(InvalidKeyException::new, pk); } } public static class EdDSAKeyFactory extends NamedKeyFactory { + @Override + protected byte[] implExpand(String pname, byte[] input) + throws InvalidKeyException { + try { + return new DerValue(input).getOctetString(); + } catch (IOException e) { + throw new InvalidKeyException(e); + } + } + public EdDSAKeyFactory() { super("EdDSA", "Ed25519", "Ed448"); } @@ -157,7 +171,10 @@ public class NamedEdDSA { // set the high-order bit of the encoded point byte msb = (byte) (point.isXOdd() ? 0x80 : 0); encodedPoint[encodedPoint.length - 1] |= msb; - return new byte[][] { encodedPoint, sk }; + return new byte[][] { + encodedPoint, + new DerOutputStream().putOctetString(sk).toByteArray(), + sk}; } private static void swap(byte[] arr, int i, int j) { diff --git a/test/jdk/sun/security/provider/NamedKeyFactoryTest.java b/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java similarity index 85% rename from test/jdk/sun/security/provider/NamedKeyFactoryTest.java rename to test/jdk/sun/security/provider/named/NamedKeyFactoryTest.java index 1ca179bc046..e58809fcb69 100644 --- a/test/jdk/sun/security/provider/NamedKeyFactoryTest.java +++ b/test/jdk/sun/security/provider/named/NamedKeyFactoryTest.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 @@ -23,7 +23,7 @@ /* * @test - * @bug 8340327 + * @bug 8340327 8347938 * @modules java.base/sun.security.x509 * java.base/sun.security.pkcs * java.base/sun.security.provider @@ -41,10 +41,13 @@ import sun.security.x509.NamedX509Key; import java.security.*; import java.security.spec.*; +import java.util.Arrays; public class NamedKeyFactoryTest { private static final SeededSecureRandom RAND = SeededSecureRandom.one(); + private static final byte[] RAW_SK = RAND.nBytes(16); + private static final byte[] RAW_PK = RAND.nBytes(16); public static void main(String[] args) throws Exception { Security.addProvider(new ProviderImpl()); @@ -78,8 +81,8 @@ public class NamedKeyFactoryTest { g.initialize(new NamedParameterSpec("ShA-256")); checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256"); - var pk = new NamedX509Key("sHa", "ShA-256", RAND.nBytes(2)); - var sk = new NamedPKCS8Key("sHa", "SHa-256", RAND.nBytes(2)); + var pk = new NamedX509Key("sHa", "ShA-256", RAW_PK); + var sk = NamedPKCS8Key.internalCreate("sHa", "SHa-256", RAW_SK, null); checkKey(pk, "sHa", "ShA-256"); checkKey(sk, "sHa", "SHa-256"); @@ -134,25 +137,27 @@ public class NamedKeyFactoryTest { Asserts.assertEquals("RAW", srk2.getFormat()); Asserts.assertEqualsByteArray(srk2.getEncoded(), sk.getRawBytes()); + checkKey(kf2.generatePrivate(srk), "SHA", "SHA-256"); Asserts.assertEqualsByteArray(kf2.generatePrivate(srk).getEncoded(), sk.getEncoded()); Utils.runAndCheckException(() -> kf.generatePrivate(srk), InvalidKeySpecException.class); // no pname + checkKey(kf2.generatePrivate(srk), "SHA", "SHA-256"); Asserts.assertEqualsByteArray(kf2.generatePrivate(srk2).getEncoded(), sk.getEncoded()); Utils.runAndCheckException(() -> kf.generatePrivate(srk2), InvalidKeySpecException.class); // no pname var pk1 = new PublicKey() { public String getAlgorithm() { return "SHA"; } public String getFormat() { return "RAW"; } - public byte[] getEncoded() { return RAND.nBytes(2); } + public byte[] getEncoded() { return RAW_PK; } }; var pk2 = new PublicKey() { public String getAlgorithm() { return "sHA-256"; } public String getFormat() { return "RAW"; } - public byte[] getEncoded() { return RAND.nBytes(2); } + public byte[] getEncoded() { return RAW_PK; } }; var pk3 = new PublicKey() { public String getAlgorithm() { return "SHA"; } public String getFormat() { return "RAW"; } - public byte[] getEncoded() { return RAND.nBytes(2); } + public byte[] getEncoded() { return RAW_PK; } public AlgorithmParameterSpec getParams() { return new NamedParameterSpec("sHA-256"); } }; @@ -167,17 +172,17 @@ public class NamedKeyFactoryTest { var sk1 = new PrivateKey() { public String getAlgorithm() { return "SHA"; } public String getFormat() { return "RAW"; } - public byte[] getEncoded() { return RAND.nBytes(2); } + public byte[] getEncoded() { return RAW_SK; } }; var sk2 = new PrivateKey() { public String getAlgorithm() { return "sHA-256"; } public String getFormat() { return "RAW"; } - public byte[] getEncoded() { return RAND.nBytes(2); } + public byte[] getEncoded() { return RAW_SK; } }; var sk3 = new PrivateKey() { public String getAlgorithm() { return "SHA"; } public String getFormat() { return "RAW"; } - public byte[] getEncoded() { return RAND.nBytes(2); } + public byte[] getEncoded() { return RAW_SK; } public AlgorithmParameterSpec getParams() { return new NamedParameterSpec("sHA-256"); } }; @@ -201,6 +206,14 @@ public class NamedKeyFactoryTest { if (k instanceof AsymmetricKey ak && ak.getParams() instanceof NamedParameterSpec nps) { Asserts.assertEquals(pname, nps.getName()); } + if (k instanceof NamedPKCS8Key nsk) { + var raw = nsk.getRawBytes(); + Asserts.assertEqualsByteArray(Arrays.copyOf(RAW_SK, raw.length), raw); + } + if (k instanceof NamedX509Key npk) { + var raw = npk.getRawBytes(); + Asserts.assertEqualsByteArray(Arrays.copyOf(RAW_PK, raw.length), raw); + } } // Provider @@ -220,15 +233,24 @@ public class NamedKeyFactoryTest { public KF() { super("SHA", "SHA-256", "SHA-512"); } - } - public static class KF1 extends NamedKeyFactory { - public KF1() { - super("SHA", "SHA-256"); + + public KF(String name) { + super("SHA", name); + } + + @Override + protected byte[] implExpand(String pname, byte[] input) throws InvalidKeyException { + return null; } } - public static class KF2 extends NamedKeyFactory { + public static class KF1 extends KF { + public KF1() { + super("SHA-256"); + } + } + public static class KF2 extends KF { public KF2() { - super("SHA", "SHA-512"); + super("SHA-512"); } } public static class KPG extends NamedKeyPairGenerator { @@ -243,8 +265,8 @@ public class NamedKeyFactoryTest { @Override public byte[][] implGenerateKeyPair(String name, SecureRandom sr) { var out = new byte[2][]; - out[0] = RAND.nBytes(name.endsWith("256") ? 2 : 4); - out[1] = RAND.nBytes(name.endsWith("256") ? 2 : 4); + out[0] = name.endsWith("256") ? Arrays.copyOf(RAW_PK, 8) : RAW_PK; + out[1] = name.endsWith("256") ? Arrays.copyOf(RAW_SK, 8) : RAW_SK; return out; } } diff --git a/test/jdk/sun/security/provider/named/NamedKeys.java b/test/jdk/sun/security/provider/named/NamedKeys.java new file mode 100644 index 00000000000..e46cd981c70 --- /dev/null +++ b/test/jdk/sun/security/provider/named/NamedKeys.java @@ -0,0 +1,103 @@ +/* + * 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 8347938 + * @modules java.base/sun.security.pkcs + * java.base/sun.security.x509 + * @library /test/lib + * @summary check the Named***Key behavior + */ +import jdk.test.lib.Asserts; +import jdk.test.lib.security.SeededSecureRandom; +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.x509.NamedX509Key; + +import java.util.Arrays; + +public class NamedKeys { + public static void main(String[] args) throws Exception { + + // This test uses fictional key algorithms SHA and SHA-256, + // simply because they look like a family name and parameter + // set name and SHA-256 already have its OID defined. + + var r = SeededSecureRandom.one(); + var raw = r.nBytes(32); + + // Create a key using raw bytes + var sk = NamedPKCS8Key.internalCreate("SHA", "SHA-256", raw, null); + var enc = sk.getEncoded(); + + // The raw bytes array is re-used + Asserts.assertTrue(sk.getRawBytes() == sk.getRawBytes()); + // but the encoding is different + Asserts.assertTrue(sk.getEncoded() != sk.getEncoded()); + + // When source change + Arrays.fill(raw, (byte)0); + // Internal raw bytes also changes + Asserts.assertEqualsByteArray(sk.getRawBytes(), new byte[32]); + // No guarantee on getEncoded() output, could be cached + + // Create a key using encoding + var sk1 = new NamedPKCS8Key("SHA", enc, null); + var sk2 = new NamedPKCS8Key("SHA", enc, null); + var raw1 = sk1.getRawBytes(); + Asserts.assertTrue(raw1 != sk2.getRawBytes()); + Asserts.assertTrue(sk1.getEncoded() != sk2.getEncoded()); + + var encCopy = enc.clone(); // store a copy + Arrays.fill(enc, (byte)0); // clean the source and the key unchanged + Asserts.assertEqualsByteArray(encCopy, sk1.getEncoded()); + + // Same with public key + // Create a key using raw bytes + raw = r.nBytes(32); + var pk = new NamedX509Key("SHA", "SHA-256", raw); + enc = pk.getEncoded().clone(); + + // The raw bytes array is re-used + Asserts.assertTrue(pk.getRawBytes() == pk.getRawBytes()); + // but the encoding is different + Asserts.assertTrue(pk.getEncoded() != pk.getEncoded()); + + // When source change + Arrays.fill(raw, (byte)0); + // Internal raw bytes also changes + Asserts.assertEqualsByteArray(pk.getRawBytes(), new byte[32]); + // No guarantee on getEncoded() output, could be cached + + // Create a key using encoding + var pk1 = new NamedX509Key("SHA", enc); + var pk2 = new NamedX509Key("SHA", enc); + raw1 = pk1.getRawBytes(); + Asserts.assertTrue(raw1 != pk2.getRawBytes()); + Asserts.assertTrue(pk1.getEncoded() != pk2.getEncoded()); + + encCopy = enc.clone(); // store a copy + Arrays.fill(enc, (byte)0); // clean the source and the key unchanged + Asserts.assertEqualsByteArray(encCopy, pk1.getEncoded()); + } +} diff --git a/test/jdk/sun/security/provider/pqc/PrivateKeyEncodings.java b/test/jdk/sun/security/provider/pqc/PrivateKeyEncodings.java new file mode 100644 index 00000000000..25060d0b74e --- /dev/null +++ b/test/jdk/sun/security/provider/pqc/PrivateKeyEncodings.java @@ -0,0 +1,227 @@ +/* + * 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 8347938 + * @library /test/lib + * @summary ensure ML-KEM and ML-DSA encodings consistent with + * draft-ietf-lamps-kyber-certificates-11 and RFC 9881 + * @modules java.base/com.sun.crypto.provider + * java.base/sun.security.pkcs + * java.base/sun.security.provider + * @run main/othervm PrivateKeyEncodings + */ +import com.sun.crypto.provider.ML_KEM_Impls; +import jdk.test.lib.Asserts; +import jdk.test.lib.security.RepositoryFileReader; +import jdk.test.lib.security.FixedSecureRandom; +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.provider.ML_DSA_Impls; + +import javax.crypto.KEM; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.cert.CertificateFactory; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.NamedParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class PrivateKeyEncodings { + + public static void main(String[] args) throws Exception { + // Example keys and certificates draft-ietf-lamps-kyber-certificates-11, Appendix B + // (https://datatracker.ietf.org/doc/html/draft-ietf-lamps-kyber-certificates-11#autoid-17) + // and RFC 9881, Appendix C.3 + // (https://datatracker.ietf.org/doc/html/rfc9881#name-example-certificates) + // + // These data can be retrieved from the following GitHub releases: + // https://github.com/lamps-wg/kyber-certificates/releases/tag/draft-ietf-lamps-kyber-certificates-11 + // https://github.com/lamps-wg/dilithium-certificates/releases/tag/draft-ietf-lamps-dilithium-certificates-13 + // + // Although the release tags include "draft", these values are the + // same as those in the final RFC 9881. + try (var kemReader = RepositoryFileReader.of(RepositoryFileReader.KYBER_CERTIFICATES.class, + "kyber-certificates-draft-ietf-lamps-kyber-certificates-11/"); + var dsaReader = RepositoryFileReader.of(RepositoryFileReader.DILITHIUM_CERTIFICATES.class, + "dilithium-certificates-draft-ietf-lamps-dilithium-certificates-13/")) { + good(kemReader, dsaReader); + badkem(kemReader); + baddsa(dsaReader); + } + } + + static void badkem(RepositoryFileReader f) throws Exception { + var kf = KeyFactory.getInstance("ML-KEM"); + + // The first ML-KEM-512-PrivateKey example includes the both CHOICE, + // i.e., both seed and expandedKey are included. The seed and expanded + // values can be checked for inconsistencies. + Asserts.assertThrows(InvalidKeySpecException.class, () -> + kf.generatePrivate(new PKCS8EncodedKeySpec( + readData(f, "example/bad-ML-KEM-512-1.priv")))); + + // The second ML-KEM-512-PrivateKey example includes only expandedKey. + // The expanded private key has a mutated s_0 and a valid public key hash, + // but a pairwise consistency check would find that the public key + // fails to match private. + var k2 = kf.generatePrivate(new PKCS8EncodedKeySpec( + readData(f, "example/bad-ML-KEM-512-2.priv"))); + var pk2 = ML_KEM_Impls.privKeyToPubKey((NamedPKCS8Key) k2); + var enc = KEM.getInstance("ML-KEM").newEncapsulator(pk2).encapsulate(); + var dk = KEM.getInstance("ML-KEM").newDecapsulator(k2).decapsulate(enc.encapsulation()); + Asserts.assertNotEqualsByteArray(enc.key().getEncoded(), dk.getEncoded()); + + // The third ML-KEM-512-PrivateKey example includes only expandedKey. + // The expanded private key has a mutated H(ek); both a public key + // digest check and a pairwise consistency check should fail. + var k3 = kf.generatePrivate(new PKCS8EncodedKeySpec( + readData(f, "example/bad-ML-KEM-512-3.priv"))); + Asserts.assertThrows(InvalidKeyException.class, () -> + KEM.getInstance("ML-KEM").newDecapsulator(k3)); + + // The fourth ML-KEM-512-PrivateKey example includes the both CHOICE, + // i.e., both seed and expandedKey are included. There is mismatch + // of the seed and expanded private key in only the z implicit rejection + // secret; here the private and public vectors match and the pairwise + // consistency check passes, but z is different. + Asserts.assertThrows(InvalidKeySpecException.class, () -> + kf.generatePrivate(new PKCS8EncodedKeySpec( + readData(f, "example/bad-ML-KEM-512-4.priv")))); + } + + static void baddsa(RepositoryFileReader f) throws Exception { + var kf = KeyFactory.getInstance("ML-DSA"); + + // The first ML-DSA-PrivateKey example includes the both CHOICE, i.e., + // both seed and expandedKey are included. The seed and expanded values + // can be checked for inconsistencies. + Asserts.assertThrows(InvalidKeySpecException.class, () -> + kf.generatePrivate(new PKCS8EncodedKeySpec( + readData(f, "examples/bad-ML-DSA-44-1.priv")))); + + // The second ML-DSA-PrivateKey example includes only expandedKey. + // The public key fails to match the tr hash value in the private key. + var k2 = kf.generatePrivate(new PKCS8EncodedKeySpec( + readData(f, "examples/bad-ML-DSA-44-2.priv"))); + Asserts.assertThrows(IllegalArgumentException.class, () -> + ML_DSA_Impls.privKeyToPubKey((NamedPKCS8Key) k2)); + + // The third ML-DSA-PrivateKey example also includes only expandedKey. + // The private s_1 and s_2 vectors imply a t vector whose private low + // bits do not match the t_0 vector portion of the private key + // (its high bits t_1 are the primary content of the public key). + var k3 = kf.generatePrivate(new PKCS8EncodedKeySpec( + readData(f, "examples/bad-ML-DSA-44-3.priv"))); + Asserts.assertThrows(IllegalArgumentException.class, () -> + ML_DSA_Impls.privKeyToPubKey((NamedPKCS8Key) k3)); + } + + static void good(RepositoryFileReader kemReader, RepositoryFileReader dsaReader) + throws Exception { + + var seed = new byte[64]; + for (var i = 0; i < seed.length; i++) { + seed[i] = (byte) i; + } + var cf = CertificateFactory.getInstance("X.509"); + var allPublicKeys = new HashMap(); + + for (var pname: List.of("ML-DSA-44", "ML-DSA-65", "ML-DSA-87", // DSA first, will sign KEM + "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024")) { + + var isKem = pname.startsWith("ML-KEM"); + KeyPairGenerator g = KeyPairGenerator.getInstance(isKem ? "ML-KEM" : "ML-DSA"); + var prop = isKem ? "mlkem" : "mldsa"; + var f = isKem ? kemReader : dsaReader; + var example = isKem ? "example/" : "examples/"; + + g.initialize(new NamedParameterSpec(pname), new FixedSecureRandom(seed)); + var pk = g.generateKeyPair().getPublic(); + allPublicKeys.put(pname, pk); + Asserts.assertEqualsByteArray(readData(f, example + pname + ".pub"), pk.getEncoded()); + + var in = new ByteArrayInputStream(readData(f, example + pname + ".crt")); + var c = cf.generateCertificate(in); + var signer = switch (pname) { + case "ML-KEM-512" -> allPublicKeys.get("ML-DSA-44"); + case "ML-KEM-768" -> allPublicKeys.get("ML-DSA-65"); + case "ML-KEM-1024" -> allPublicKeys.get("ML-DSA-87"); + default -> c.getPublicKey(); + }; + c.verify(signer); + Asserts.assertEquals(c.getPublicKey(), pk); + + for (var type : List.of("seed", "expandedkey", "both")) { + System.err.println(pname + " " + type); + System.setProperty("jdk." + prop + ".pkcs8.encoding", type); + g.initialize(new NamedParameterSpec(pname), new FixedSecureRandom(seed)); + var sk = g.generateKeyPair().getPrivate(); + if (type.equals("expandedkey")) type = "expanded"; + Asserts.assertEqualsByteArray( + readData(f, example + pname + "-" + type + ".priv"), sk.getEncoded()); + checkInterop(pk, sk); + } + } + } + + // Ensures pk and sk interop with each other + static void checkInterop(PublicKey pk, PrivateKey sk) throws Exception { + if (pk.getAlgorithm().startsWith("ML-KEM")) { + var kem = KEM.getInstance("ML-KEM"); + var enc = kem.newEncapsulator(pk).encapsulate(); + var k = kem.newDecapsulator(sk).decapsulate(enc.encapsulation()); + Asserts.assertEqualsByteArray(k.getEncoded(), enc.key().getEncoded()); + } else { + var msg = "hello".getBytes(StandardCharsets.UTF_8); + var s = Signature.getInstance("ML-DSA"); + s.initSign(sk); + s.update(msg); + var sig = s.sign(); + s.initVerify(pk); + s.update(msg); + Asserts.assertTrue(s.verify(sig)); + } + } + + static byte[] readData(RepositoryFileReader f, String entry) throws Exception { + byte[] data = f.read(entry); + var pem = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(data))) + .lines() + .filter(s -> !s.contains("-----")) + .collect(Collectors.joining()); + return Base64.getMimeDecoder().decode(pem); + } +} diff --git a/test/jdk/sun/security/provider/pqc/SeedOrExpanded.java b/test/jdk/sun/security/provider/pqc/SeedOrExpanded.java new file mode 100644 index 00000000000..0a5b462b037 --- /dev/null +++ b/test/jdk/sun/security/provider/pqc/SeedOrExpanded.java @@ -0,0 +1,194 @@ +/* + * 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 8347938 + * @library /test/lib + * @modules java.base/com.sun.crypto.provider + * java.base/sun.security.pkcs + * java.base/sun.security.provider + * java.base/sun.security.util + * java.base/sun.security.x509 + * @summary check key reading compatibility + * @run main/othervm SeedOrExpanded + */ + +import com.sun.crypto.provider.ML_KEM_Impls; +import jdk.test.lib.Asserts; +import jdk.test.lib.security.DerUtils; +import jdk.test.lib.security.FixedSecureRandom; +import jdk.test.lib.security.SeededSecureRandom; +import sun.security.pkcs.NamedPKCS8Key; +import sun.security.provider.ML_DSA_Impls; +import sun.security.util.DerValue; + +import javax.crypto.KEM; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; + +public class SeedOrExpanded { + + static final SeededSecureRandom RAND = SeededSecureRandom.one(); + + public static void main(String[] args) throws Exception { + test("mlkem", "ML-KEM-768"); + test("mldsa", "ML-DSA-65"); + } + + static void test(String type, String alg) throws Exception { + + var seed = RAND.nBytes(alg.contains("ML-KEM") ? 64 : 32); + var g = KeyPairGenerator.getInstance(alg); + + // Generation + + g.initialize(-1, new FixedSecureRandom(seed)); + var kp = g.generateKeyPair(); + var pk = kp.getPublic(); + var kDefault = kp.getPrivate(); + + // Property value is case-insensitive + System.setProperty("jdk." + type + ".pkcs8.encoding", "SEED"); + g.initialize(-1, new FixedSecureRandom(seed)); + var kSeed = g.generateKeyPair().getPrivate(); + System.setProperty("jdk." + type + ".pkcs8.encoding", "expandedkey"); + g.initialize(-1, new FixedSecureRandom(seed)); + var kExpanded = g.generateKeyPair().getPrivate(); + System.setProperty("jdk." + type + ".pkcs8.encoding", "boTH"); + g.initialize(-1, new FixedSecureRandom(seed)); + var kBoth = g.generateKeyPair().getPrivate(); + + // Invalid property value + System.setProperty("jdk." + type + ".pkcs8.encoding", "bogus"); + g.initialize(-1, new FixedSecureRandom(seed)); + Asserts.assertThrows(IllegalArgumentException.class, + () -> g.generateKeyPair().getPrivate()); + + byte[] kExpandedEncoded = kExpanded.getEncoded(); + byte[] kSeedEncoded = kSeed.getEncoded(); + byte[] kBothEncoded = kBoth.getEncoded(); + + // Ensure tags match the CHOICE definition + Asserts.assertEquals((byte) 0x80, DerUtils.innerDerValue(kSeedEncoded, "2c").tag); + Asserts.assertEquals((byte) 0x04, DerUtils.innerDerValue(kExpandedEncoded, "2c").tag); + Asserts.assertEquals((byte) 0x30, DerUtils.innerDerValue(kBothEncoded, "2c").tag); + + byte[] seedData = DerUtils.innerDerValue(kSeedEncoded, "2c") + .withTag(DerValue.tag_OctetString).getOctetString(); + byte[] expandedData = DerUtils.innerDerValue(kExpandedEncoded, "2c").getOctetString(); + + Asserts.assertEqualsByteArray(seed, seedData); + Asserts.assertEqualsByteArray( + seedData, + DerUtils.innerDerValue(kBothEncoded, "2c0").getOctetString()); + Asserts.assertEqualsByteArray( + expandedData, + DerUtils.innerDerValue(kBothEncoded, "2c1").getOctetString()); + + // Ensure seedToExpanded correctly called + if (alg.contains("ML-KEM")) { + Asserts.assertEqualsByteArray(expandedData, + ML_KEM_Impls.seedToExpanded(alg, seedData)); + } else { + Asserts.assertEqualsByteArray(expandedData, + ML_DSA_Impls.seedToExpanded(alg, seedData)); + } + + test(alg, pk, kSeed); + test(alg, pk, kExpanded); + test(alg, pk, kBoth); + + var kf = KeyFactory.getInstance(alg); + + System.setProperty("jdk." + type + ".pkcs8.encoding", "seed"); + Asserts.assertEqualsByteArray( + test(alg, pk, kf.translateKey(kBoth)).getEncoded(), + kSeedEncoded); + Asserts.assertTrue(kf.translateKey(kSeed) == kSeed); + Asserts.assertThrows(InvalidKeyException.class, () -> kf.translateKey(kExpanded)); + + System.setProperty("jdk." + type + ".pkcs8.encoding", "expandedkey"); + Asserts.assertEqualsByteArray( + test(alg, pk, kf.translateKey(kBoth)).getEncoded(), + kExpandedEncoded); + Asserts.assertEqualsByteArray( + test(alg, pk, kf.translateKey(kSeed)).getEncoded(), + kExpandedEncoded); + Asserts.assertTrue(kf.translateKey(kExpanded) == kExpanded); + + System.setProperty("jdk." + type + ".pkcs8.encoding", "both"); + Asserts.assertTrue(kf.translateKey(kBoth) == kBoth); + Asserts.assertEqualsByteArray( + test(alg, pk, kf.translateKey(kSeed)).getEncoded(), + kBothEncoded); + Asserts.assertThrows(InvalidKeyException.class, () -> kf.translateKey(kExpanded)); + + // The following makes sure key is not mistakenly cleaned during + // translations. + var xk = new PrivateKey() { + public String getAlgorithm() { return alg; } + public String getFormat() { return "PKCS#8"; } + public byte[] getEncoded() { return kBothEncoded.clone(); } + }; + test(alg, pk, xk); + var xk2 = (PrivateKey) kf.translateKey(xk); + test(alg, pk, xk2); + test(alg, pk, xk); + } + + static PrivateKey test(String alg, PublicKey pk, Key k) throws Exception { + var sk = (PrivateKey) k; + if (alg.contains("ML-KEM")) { + var kem = KEM.getInstance("ML-KEM"); + var e = kem.newEncapsulator(pk, RAND); + var enc = e.encapsulate(); + var k1 = kem.newDecapsulator(sk).decapsulate(enc.encapsulation()); + Asserts.assertEqualsByteArray(k1.getEncoded(), enc.key().getEncoded()); + if (k instanceof NamedPKCS8Key npk) { + Asserts.assertEqualsByteArray( + ML_KEM_Impls.privKeyToPubKey(npk).getEncoded(), pk.getEncoded()); + } + } else { + var s = Signature.getInstance("ML-DSA"); + var rnd = RAND.nBytes(32); // randomness for signature generation + var msg = RAND.nBytes(20); + s.initSign(sk, new FixedSecureRandom(rnd)); + s.update(msg); + var sig1 = s.sign(); + s.initVerify(pk); + s.update(msg); + Asserts.assertTrue(s.verify(sig1)); + if (k instanceof NamedPKCS8Key npk) { + Asserts.assertEqualsByteArray( + ML_DSA_Impls.privKeyToPubKey(npk).getEncoded(), pk.getEncoded()); + } + } + return sk; + } +} diff --git a/test/lib/jdk/test/lib/process/Proc.java b/test/lib/jdk/test/lib/process/Proc.java index 2fe802fed6c..a989906b2ab 100644 --- a/test/lib/jdk/test/lib/process/Proc.java +++ b/test/lib/jdk/test/lib/process/Proc.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 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 @@ -256,6 +256,15 @@ public class Proc { } } } + String patchPath = System.getProperty("test.patch.path"); + if (patchPath != null) { + try (var subs = Files.newDirectoryStream(Path.of(patchPath))) { + for (var sub : subs) { + var name = sub.getFileName(); + cmd.add("--patch-module=" + name + "=" + sub); + } + } + } var lcp = fullcp(); if (lcp != null) { diff --git a/test/lib/jdk/test/lib/security/RepositoryFileReader.java b/test/lib/jdk/test/lib/security/RepositoryFileReader.java index 4eefc7d82db..04bc3c88d2a 100644 --- a/test/lib/jdk/test/lib/security/RepositoryFileReader.java +++ b/test/lib/jdk/test/lib/security/RepositoryFileReader.java @@ -148,4 +148,13 @@ public sealed interface RepositoryFileReader extends AutoCloseable { unpack = false) public static class CMS_ML_DSA { } + + @Artifact( + organization = "jpg.tests.jdk.repos.lamps-wg", + name = "kyber-certificates", + revision = "29f3215", + extension = "zip", + unpack = false) + public static class KYBER_CERTIFICATES { + } } From 58d2edb9fc1bb68363e697b43be04c493ead81c5 Mon Sep 17 00:00:00 2001 From: Koushik Thirupattur Date: Tue, 3 Feb 2026 19:09:19 +0000 Subject: [PATCH 107/215] 8370688: java.util.jar.JarEntry.getCodeSigners() and getCertificates() should specify that they return a copy of the arrays Reviewed-by: jpai, mullan, liach --- .../share/classes/java/util/jar/JarEntry.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/util/jar/JarEntry.java b/src/java.base/share/classes/java/util/jar/JarEntry.java index ff0750a3342..6037ee243e5 100644 --- a/src/java.base/share/classes/java/util/jar/JarEntry.java +++ b/src/java.base/share/classes/java/util/jar/JarEntry.java @@ -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 @@ -114,8 +114,10 @@ public class JarEntry extends ZipEntry { * validate each signer's certificate chain, and determining whether * to trust the entry signed by the signers. * - * @return the {@code Certificate} objects for this entry, or - * {@code null} if none. + * @implSpec If non-null, this implementation returns a new array each time + * this method is invoked. + * + * @return the {@code Certificate} objects for this entry, or {@code null} if none. * */ public Certificate[] getCertificates() { @@ -139,8 +141,10 @@ public class JarEntry extends ZipEntry { * validate each signer's certificate chain, and determining whether * to trust the entry signed by the signers. * - * @return the {@code CodeSigner} objects for this entry, or - * {@code null} if none. + * @implSpec If non-null, this implementation returns a new array each time + * this method is invoked. + * + * @return the {@code CodeSigner} objects for this entry, or {@code null} if none. * * @since 1.5 */ From 5fea0741a6b7ff7e3a41844c86e422c0f0582333 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Tue, 3 Feb 2026 19:24:41 +0000 Subject: [PATCH 108/215] 8376297: ArrayIndexOutOfBoundsException Not Documented for SinglePixelPackedSampleModel.getSampleSize(int) Reviewed-by: aivanov, serb, azvegint, kizune --- .../java/awt/image/ComponentSampleModel.java | 8 +- .../image/MultiPixelPackedSampleModel.java | 8 +- .../image/SinglePixelPackedSampleModel.java | 9 +- .../jdk/java/awt/image/GetSampleSizeTest.java | 85 +++++++++++++++++++ 4 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 test/jdk/java/awt/image/GetSampleSizeTest.java diff --git a/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java b/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java index d15a8d214aa..4460b0fc101 100644 --- a/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/ComponentSampleModel.java @@ -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 @@ -503,7 +503,11 @@ public class ComponentSampleModel extends SampleModel } /** Returns the number of bits per sample for the specified band. - * @param band the specified band + *

    + * Since all bands of a {@code ComponentSampleModel} are the same + * size, this method ignores the {@code band} parameter and returns + * the size of the first (0th) band. + * @param band the specified band (ignored) * @return the number of bits per sample for the specified band. */ public final int getSampleSize(int band) { diff --git a/src/java.desktop/share/classes/java/awt/image/MultiPixelPackedSampleModel.java b/src/java.desktop/share/classes/java/awt/image/MultiPixelPackedSampleModel.java index a79ddf52cd9..d8f97562c6c 100644 --- a/src/java.desktop/share/classes/java/awt/image/MultiPixelPackedSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/MultiPixelPackedSampleModel.java @@ -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 @@ -235,7 +235,11 @@ public class MultiPixelPackedSampleModel extends SampleModel /** * Returns the number of bits per sample for the specified band. - * @param band the specified band + *

    + * Since {@code MultiPixelPackedSampleModel} has only one band, + * this method ignores the {@code band} parameter and returns + * the sample size of the single band. + * @param band the specified band (ignored) * @return the number of bits per sample for the specified band. */ public int getSampleSize(int band) { diff --git a/src/java.desktop/share/classes/java/awt/image/SinglePixelPackedSampleModel.java b/src/java.desktop/share/classes/java/awt/image/SinglePixelPackedSampleModel.java index d12c151a8fd..2ab226ea227 100644 --- a/src/java.desktop/share/classes/java/awt/image/SinglePixelPackedSampleModel.java +++ b/src/java.desktop/share/classes/java/awt/image/SinglePixelPackedSampleModel.java @@ -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 @@ -250,7 +250,12 @@ public class SinglePixelPackedSampleModel extends SampleModel return bitSizes.clone(); } - /** Returns the number of bits per sample for the specified band. */ + /** Returns the number of bits per sample for the specified band. + * @param band the specified band + * @return the size of the samples of the specified band. + * @throws ArrayIndexOutOfBoundsException if the {@code band} index + * is less than zero or greater than or equal to {@code getNumBands()} + */ public int getSampleSize(int band) { return bitSizes[band]; } diff --git a/test/jdk/java/awt/image/GetSampleSizeTest.java b/test/jdk/java/awt/image/GetSampleSizeTest.java new file mode 100644 index 00000000000..dcf6d1e6ed5 --- /dev/null +++ b/test/jdk/java/awt/image/GetSampleSizeTest.java @@ -0,0 +1,85 @@ +/* + * 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. + */ + +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.MultiPixelPackedSampleModel; +import java.awt.image.SinglePixelPackedSampleModel; + +/* + * @test + * @bug 8376297 + * @summary Test SampleModel.getSampleSize(int) + */ + +public class GetSampleSizeTest { + + public static void main(String[] args) { + + final int width = 10; + final int height = 10; + int[] bandOffsets = {0, 0}; + int[] bitMask = {0x00ff0000, 0x0000ff00, 0xff, 0x0}; + + { + ComponentSampleModel csm = + new ComponentSampleModel(DataBuffer.TYPE_BYTE, + width, height, 1, width, bandOffsets); + int numBands = csm.getNumBands(); + System.out.println("CSM numBands = " + numBands); + if (numBands != 2) { + throw new RuntimeException("Unexpected numBands"); + } + System.out.println("CSM sample size = " + csm.getSampleSize(numBands)); + } + + { + MultiPixelPackedSampleModel mppsm = + new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, width, height, 4); + int numBands = mppsm.getNumBands(); + System.out.println("MPPSM numBands = " + numBands); + if (numBands != 1) { + throw new RuntimeException("Unexpected numBands"); + } + System.out.println("MPPSM sample size = " + mppsm.getSampleSize(numBands)); + } + + { + SinglePixelPackedSampleModel sppsm = + new SinglePixelPackedSampleModel(DataBuffer.TYPE_BYTE, width, height, bitMask); + int numBands = sppsm.getNumBands(); + System.out.println("SPPSM numBands = " + numBands); + if (numBands != 4) { + throw new RuntimeException("Unexpected numBands"); + } + try { + System.out.println("SPPSM sample size = " + sppsm.getSampleSize(numBands)); + throw new RuntimeException("No expected AIOBE"); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Got expected AIOBE for SPPSM"); + } + } + + } +} + From f3c8502e38de714caab8edd895113528f1ea4f5e Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 4 Feb 2026 00:51:29 +0000 Subject: [PATCH 109/215] 8227493: Return a more useful error message from lookupAllHostAddr if getaddrinfo results in EAI_SYSTEM error Reviewed-by: dfuchs, djelinski, michaelm --- .../unix/native/libnet/Inet4AddressImpl.c | 6 ++- .../unix/native/libnet/Inet6AddressImpl.c | 6 ++- .../unix/native/libnet/net_util_md.c | 37 ++++++++++++++++--- .../unix/native/libnet/net_util_md.h | 5 ++- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/java.base/unix/native/libnet/Inet4AddressImpl.c b/src/java.base/unix/native/libnet/Inet4AddressImpl.c index 9bddbcaede7..e99dfd89411 100644 --- a/src/java.base/unix/native/libnet/Inet4AddressImpl.c +++ b/src/java.base/unix/native/libnet/Inet4AddressImpl.c @@ -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 @@ -112,6 +112,8 @@ Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, error == EAI_SYSTEM && errno == EINTR); if (error) { + // capture the errno from getaddrinfo + const int sys_errno = errno; #if defined(MACOSX) // If getaddrinfo fails try getifaddrs, see bug 8170910. // java_net_spi_InetAddressResolver_LookupPolicy_IPV4_FIRST and no ordering is ok @@ -122,7 +124,7 @@ Java_java_net_Inet4AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, } #endif // report error - NET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error); + NET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error, sys_errno); goto cleanupAndReturn; } else { int i = 0; diff --git a/src/java.base/unix/native/libnet/Inet6AddressImpl.c b/src/java.base/unix/native/libnet/Inet6AddressImpl.c index 8dce4f9cc6b..e0963c8dc3e 100644 --- a/src/java.base/unix/native/libnet/Inet6AddressImpl.c +++ b/src/java.base/unix/native/libnet/Inet6AddressImpl.c @@ -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 @@ -231,6 +231,8 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, error == EAI_SYSTEM && errno == EINTR); if (error) { + // capture the errno from getaddrinfo + const int sys_errno = errno; #if defined(MACOSX) // if getaddrinfo fails try getifaddrs ret = lookupIfLocalhost(env, hostname, JNI_TRUE, characteristics); @@ -239,7 +241,7 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this, } #endif // report error - NET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error); + NET_ThrowUnknownHostExceptionWithGaiError(env, hostname, error, sys_errno); goto cleanupAndReturn; } else { int i = 0, inetCount = 0, inet6Count = 0, inetIndex = 0, diff --git a/src/java.base/unix/native/libnet/net_util_md.c b/src/java.base/unix/native/libnet/net_util_md.c index 48cc1a7bb02..f7b690f1032 100644 --- a/src/java.base/unix/native/libnet/net_util_md.c +++ b/src/java.base/unix/native/libnet/net_util_md.c @@ -178,13 +178,21 @@ jint reuseport_supported(int ipv6_available) void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env, const char* hostname, - int gai_error) + int gai_error, + int sys_errno) { int size; char *buf; + const char *sys_errno_string = NULL; const char *error_string = gai_strerror(gai_error); - if (error_string == NULL) + if (error_string == NULL) { error_string = "unknown error"; + } + if (gai_error == EAI_SYSTEM) { + // EAI_SYSTEM implies that the actual error is stored in the system errno. + // Here we get the string representation of that errno. + sys_errno_string = strerror(sys_errno); + } int enhancedExceptions = getEnhancedExceptionsAllowed(env); if (enhancedExceptions == ENH_INIT_ERROR && (*env)->ExceptionCheck(env)) { return; @@ -195,16 +203,33 @@ void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env, } else { size = 0; } - size += strlen(error_string) + 3; - + if (sys_errno_string == NULL) { + // the 3 is for the additional 3 characters - colon, space and + // the NULL termination character, that we will include in the + // message of the Exception that we construct + size += strlen(error_string) + 3; + } else { + // the 5 is for the additional 5 characters - 2 colons, 2 spaces and + // the NULL termination character, that we will include in the + // message of the Exception that we construct + size += strlen(error_string) + strlen(sys_errno_string) + 5; + } buf = (char *) malloc(size); if (buf) { jstring s; int n; if (enhancedExceptions == ENH_ENABLED) { - n = snprintf(buf, size, "%s: %s", hostname, error_string); + if (sys_errno_string == NULL) { + n = snprintf(buf, size, "%s: %s", hostname, error_string); + } else { + n = snprintf(buf, size, "%s: %s: %s", hostname, error_string, sys_errno_string); + } } else { - n = snprintf(buf, size, " %s", error_string); + if (sys_errno_string == NULL) { + n = snprintf(buf, size, " %s", error_string); + } else { + n = snprintf(buf, size, " %s: %s", error_string, sys_errno_string); + } } if (n >= 0) { s = JNU_NewStringPlatform(env, buf); diff --git a/src/java.base/unix/native/libnet/net_util_md.h b/src/java.base/unix/native/libnet/net_util_md.h index dca6e97755a..639cf00515f 100644 --- a/src/java.base/unix/native/libnet/net_util_md.h +++ b/src/java.base/unix/native/libnet/net_util_md.h @@ -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 @@ -76,7 +76,8 @@ typedef union { */ void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env, const char* hostname, - int gai_error); + int gai_error, + int sys_errno); void NET_ThrowByNameWithLastError(JNIEnv *env, const char *name, const char *defaultDetail); From 14a6e928ce9a10f6d85fae8db4ce303da20bde85 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Wed, 4 Feb 2026 02:04:04 +0000 Subject: [PATCH 110/215] 8376630: java/lang/ProcessBuilder/PipelineLeaksFD.java intermittent timed out Reviewed-by: rriggs --- .../java/lang/ProcessBuilder/PipelineLeaksFD.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java index 2b4cdfa9bdd..e7eaa57ec95 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.condition.EnabledIf; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.*; +import jdk.test.lib.Utils; import java.io.BufferedReader; import java.io.IOException; @@ -32,6 +33,7 @@ import java.io.Writer; import java.lang.ProcessHandle; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.TimeUnit; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -44,7 +46,8 @@ import java.util.stream.Collectors; * @bug 8289643 8291760 8291986 * @requires os.family == "mac" | (os.family == "linux" & !vm.musl) * @summary File descriptor leak detection with ProcessBuilder.startPipeline - * @run junit/othervm PipelineLeaksFD + * @library /test/lib + * @run junit/othervm/timeout=240 PipelineLeaksFD */ public class PipelineLeaksFD { @@ -236,8 +239,11 @@ public class PipelineLeaksFD { .redirectInput(lsofEmptyInput.toFile()) // empty input .redirectError(ProcessBuilder.Redirect.DISCARD) // ignored output .start()) { - int status = p.waitFor(); - assertEquals(0, status, "Process 'lsof' failed"); + boolean status = p.waitFor(Utils.adjustTimeout(120), TimeUnit.SECONDS); + if (!status) { + p.destroyForcibly(); + } + assertTrue(status, "Process 'lsof' failed"); return Files.readAllLines(lsofOutput); } catch (InterruptedException ie) { From 443cd77509bd4144ba7dfec26e3e7b2e62c799f9 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 4 Feb 2026 06:44:59 +0000 Subject: [PATCH 111/215] 8376758: Fix -Wzero-as-null-pointer-constant warnings in AIX code Reviewed-by: dholmes, jsjolen --- src/hotspot/os/aix/decoder_aix.hpp | 4 ++-- src/hotspot/os/aix/os_aix.cpp | 2 +- src/hotspot/os/aix/porting_aix.cpp | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/hotspot/os/aix/decoder_aix.hpp b/src/hotspot/os/aix/decoder_aix.hpp index 2ba3e1c5a3a..632355ccf4e 100644 --- a/src/hotspot/os/aix/decoder_aix.hpp +++ b/src/hotspot/os/aix/decoder_aix.hpp @@ -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. * Copyright (c) 2013 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -38,7 +38,7 @@ class AIXDecoder: public AbstractDecoder { virtual bool demangle(const char* symbol, char* buf, int buflen) { return false; } // use AixSymbols::get_function_name to demangle virtual bool decode(address addr, char* buf, int buflen, int* offset, const char* modulepath, bool demangle) { - return AixSymbols::get_function_name(addr, buf, buflen, offset, 0, demangle); + return AixSymbols::get_function_name(addr, buf, buflen, offset, nullptr, demangle); } virtual bool decode(address addr, char *buf, int buflen, int* offset, const void *base) { ShouldNotReachHere(); diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index d7c1911a914..0a8efbece8d 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -703,7 +703,7 @@ static void *thread_native_entry(Thread *thread) { log_info(os, thread)("Thread finished (tid: %zu, kernel thread id: %zu).", os::current_thread_id(), (uintx) kernel_thread_id); - return 0; + return nullptr; } bool os::create_thread(Thread* thread, ThreadType thr_type, diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp index 7311afc197b..b3f878fbfdd 100644 --- a/src/hotspot/os/aix/porting_aix.cpp +++ b/src/hotspot/os/aix/porting_aix.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2012, 2024 SAP SE. All rights reserved. - * 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 @@ -78,7 +78,7 @@ class fixed_strings { public: - fixed_strings() : first(0) {} + fixed_strings() : first(nullptr) {} ~fixed_strings() { node* n = first; while (n) { @@ -113,7 +113,7 @@ bool AixSymbols::get_function_name ( // information (null if not available) bool demangle // [in] whether to demangle the name ) { - struct tbtable* tb = 0; + struct tbtable* tb = nullptr; unsigned int searchcount = 0; // initialize output parameters @@ -653,10 +653,10 @@ void AixNativeCallstack::print_callstack_for_context(outputStream* st, const uco // To print the first frame, use the current value of iar: // current entry indicated by iar (the current pc) - codeptr_t cur_iar = 0; - stackptr_t cur_sp = 0; - codeptr_t cur_rtoc = 0; - codeptr_t cur_lr = 0; + codeptr_t cur_iar = nullptr; + stackptr_t cur_sp = nullptr; + codeptr_t cur_rtoc = nullptr; + codeptr_t cur_lr = nullptr; const ucontext_t* uc = (const ucontext_t*) context; @@ -926,7 +926,7 @@ static struct handletableentry* p_handletable = nullptr; static const char* rtv_linkedin_libpath() { constexpr int bufsize = 4096; static char buffer[bufsize]; - static const char* libpath = 0; + static const char* libpath = nullptr; // we only try to retrieve the libpath once. After that try we // let libpath point to buffer, which then contains a valid libpath From 1069ccebcc32e02055985e2babfa2986a2e295ca Mon Sep 17 00:00:00 2001 From: Thomas Devoogdt Date: Wed, 4 Feb 2026 06:48:59 +0000 Subject: [PATCH 112/215] 8376684: Compile OpenJDK in headless mode without required X11 libraries Reviewed-by: erikj, aivanov --- doc/building.html | 7 +++---- doc/building.md | 6 ++---- make/autoconf/libraries.m4 | 8 ++++---- make/modules/java.desktop/lib/AwtLibraries.gmk | 16 ++++++++++++++-- .../unix/native/common/awt/utility/rect.h | 4 ++-- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/doc/building.html b/doc/building.html index 8e5a7625371..534888ef667 100644 --- a/doc/building.html +++ b/doc/building.html @@ -1385,10 +1385,9 @@ dpkg-deb -x /tmp/libasound2-dev_1.0.25-4_armhf.deb .

  • can specify it by --with-alsa.

    X11

    -

    You will need X11 libraries suitable for your target system. -In most cases, using Debian's pre-built libraries work fine.

    -

    Note that X11 is needed even if you only want to build a headless -JDK.

    +

    When not building a headless JDK, you will need X11 libraries +suitable for your target system. In most cases, using Debian's +pre-built libraries work fine.

    • Go to Debian Package Search, search for the following packages for your diff --git a/doc/building.md b/doc/building.md index b626027f101..d653d36eb55 100644 --- a/doc/building.md +++ b/doc/building.md @@ -1178,10 +1178,8 @@ Note that alsa is needed even if you only want to build a headless JDK. #### X11 -You will need X11 libraries suitable for your *target* system. In most cases, -using Debian's pre-built libraries work fine. - -Note that X11 is needed even if you only want to build a headless JDK. +When not building a headless JDK, you will need X11 libraries suitable for your +*target* system. In most cases, using Debian's pre-built libraries work fine. * Go to [Debian Package Search](https://www.debian.org/distrib/packages), search for the following packages for your *target* system, and download them diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4 index 8dc3d55ed0c..5daacdc1ced 100644 --- a/make/autoconf/libraries.m4 +++ b/make/autoconf/libraries.m4 @@ -42,12 +42,12 @@ m4_include([lib-tests.m4]) AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES], [ # Check if X11 is needed - if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then - # No X11 support on windows or macosx + if test "x$OPENJDK_TARGET_OS" = xwindows || + test "x$OPENJDK_TARGET_OS" = xmacosx || + test "x$ENABLE_HEADLESS_ONLY" = xtrue; then NEEDS_LIB_X11=false else - # All other instances need X11, even if building headless only, libawt still - # needs X11 headers. + # All other instances need X11 for libawt. NEEDS_LIB_X11=true fi diff --git a/make/modules/java.desktop/lib/AwtLibraries.gmk b/make/modules/java.desktop/lib/AwtLibraries.gmk index 463e09e12dc..8b6b50b9e62 100644 --- a/make/modules/java.desktop/lib/AwtLibraries.gmk +++ b/make/modules/java.desktop/lib/AwtLibraries.gmk @@ -88,6 +88,10 @@ LIBAWT_EXTRA_HEADER_DIRS := \ LIBAWT_CFLAGS := -D__MEDIALIB_OLD_NAMES -D__USE_J2D_NAMES -DMLIB_NO_LIBSUNMATH +ifeq ($(ENABLE_HEADLESS_ONLY), true) + LIBAWT_CFLAGS += -DHEADLESS +endif + ifeq ($(call isTargetOs, windows), true) LIBAWT_CFLAGS += -EHsc -DUNICODE -D_UNICODE -DMLIB_OS64BIT LIBAWT_RCFLAGS ?= -I$(TOPDIR)/src/java.base/windows/native/launcher/icons @@ -167,11 +171,18 @@ ifeq ($(call isTargetOs, windows macosx), false) $(TOPDIR)/src/$(MODULE)/$(OPENJDK_TARGET_OS_TYPE)/native/common/awt \ # + LIBAWT_HEADLESS_EXCLUDE_FILES := \ + GLXGraphicsConfig.c \ + GLXSurfaceData.c \ + X11PMBlitLoops.c \ + X11Renderer.c \ + X11SurfaceData.c \ + # + LIBAWT_HEADLESS_EXTRA_HEADER_DIRS := \ $(LIBAWT_DEFAULT_HEADER_DIRS) \ common/awt/debug \ common/font \ - common/java2d/opengl \ java.base:libjvm \ # @@ -191,7 +202,8 @@ ifeq ($(call isTargetOs, windows macosx), false) $(eval $(call SetupJdkLibrary, BUILD_LIBAWT_HEADLESS, \ NAME := awt_headless, \ EXTRA_SRC := $(LIBAWT_HEADLESS_EXTRA_SRC), \ - EXCLUDES := medialib, \ + EXCLUDES := medialib opengl, \ + EXCLUDE_FILES := $(LIBAWT_HEADLESS_EXCLUDE_FILES), \ ONLY_EXPORTED := $(LIBAWT_HEADLESS_ONLY_EXPORTED), \ OPTIMIZATION := LOW, \ CFLAGS := -DHEADLESS=true $(CUPS_CFLAGS) $(FONTCONFIG_CFLAGS) \ diff --git a/src/java.desktop/unix/native/common/awt/utility/rect.h b/src/java.desktop/unix/native/common/awt/utility/rect.h index ceea38f4349..91b5a17ec58 100644 --- a/src/java.desktop/unix/native/common/awt/utility/rect.h +++ b/src/java.desktop/unix/native/common/awt/utility/rect.h @@ -28,7 +28,7 @@ #ifndef _AWT_RECT_H #define _AWT_RECT_H -#ifndef MACOSX +#if !defined(HEADLESS) && !defined(MACOSX) #include typedef XRectangle RECT_T; #else @@ -39,7 +39,7 @@ typedef struct { int width; int height; } RECT_T; -#endif /* !MACOSX */ +#endif /* !HEADLESS && !MACOSX */ #define RECT_EQ_X(r1,r2) ((r1).x==(r2).x && (r1).width==(r2).width) From 7e8fad625a2cdc9a4e46eb31c485de074997c7c0 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Wed, 4 Feb 2026 07:30:46 +0000 Subject: [PATCH 113/215] 8376760: VerifyJimage.java#compare intermittent failed with fastdebug Reviewed-by: liach, alanb --- test/jdk/tools/jimage/VerifyJimage.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/jdk/tools/jimage/VerifyJimage.java b/test/jdk/tools/jimage/VerifyJimage.java index 08f567cbecb..723ad089fa9 100644 --- a/test/jdk/tools/jimage/VerifyJimage.java +++ b/test/jdk/tools/jimage/VerifyJimage.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 @@ -145,15 +145,9 @@ public abstract class VerifyJimage implements Runnable { pool.execute(new ModuleResourceComparator(rootDir, modName, jimage)); } } - pool.shutdown(); - if (!pool.awaitTermination(20, TimeUnit.SECONDS)) { - failed.add("Directory verification timed out"); - } + pool.close(); } catch (IOException ex) { throw new UncheckedIOException(ex); - } catch (InterruptedException e) { - failed.add("Directory verification was interrupted"); - Thread.currentThread().interrupt(); } } From d67f72e0d55ce4da5928716fc6ab87d87516443b Mon Sep 17 00:00:00 2001 From: Mikhail Yankelevich Date: Wed, 4 Feb 2026 07:54:57 +0000 Subject: [PATCH 114/215] 8377063: Add EchoPassword.java to manual group Reviewed-by: msheppar, rhalade --- test/jdk/TEST.groups | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 27f6bb48a63..ccf745700d1 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -1,4 +1,4 @@ -# 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 @@ -664,7 +664,8 @@ jdk_core_manual_interactive = \ jdk_security_manual_interactive = \ sun/security/tools/keytool/i18n.java \ com/sun/security/auth/callback/TextCallbackHandler/Password.java \ - sun/security/krb5/config/native/TestDynamicStore.java + sun/security/krb5/config/native/TestDynamicStore.java \ + sun/security/tools/keytool/EchoPassword.java # Test sets for running inside container environment jdk_containers_extended = \ From b0829a54cd787d5e378573f69ec0b82b40602454 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 4 Feb 2026 08:24:42 +0000 Subject: [PATCH 115/215] 8372948: Store end positions directly in JCTree Reviewed-by: jlahoda, mcimadamore --- .../sun/tools/javac/api/JavacTaskImpl.java | 5 +- .../com/sun/tools/javac/api/JavacTrees.java | 4 +- .../com/sun/tools/javac/code/LintMapper.java | 23 +-- .../com/sun/tools/javac/comp/Attr.java | 5 +- .../com/sun/tools/javac/comp/Lower.java | 11 +- .../com/sun/tools/javac/jvm/CRTable.java | 10 +- .../classes/com/sun/tools/javac/jvm/Gen.java | 15 +- .../sun/tools/javac/main/JavaCompiler.java | 15 +- .../sun/tools/javac/parser/JavaTokenizer.java | 3 +- .../sun/tools/javac/parser/JavacParser.java | 127 +++--------- .../sun/tools/javac/parser/ParserFactory.java | 9 +- .../com/sun/tools/javac/tree/DCTree.java | 2 +- .../com/sun/tools/javac/tree/EndPosTable.java | 83 -------- .../com/sun/tools/javac/tree/JCTree.java | 11 +- .../com/sun/tools/javac/tree/TreeInfo.java | 60 +++--- .../tools/javac/util/DiagnosticSource.java | 13 -- .../sun/tools/javac/util/IntHashTable.java | 190 ------------------ .../sun/tools/javac/util/JCDiagnostic.java | 15 +- .../classes/com/sun/tools/javac/util/Log.java | 6 - .../jdk/javadoc/internal/tool/JavadocLog.java | 3 +- .../share/classes/jdk/jshell/ReplParser.java | 6 +- .../classes/jdk/jshell/ReplParserFactory.java | 9 +- .../share/classes/jdk/jshell/TaskFactory.java | 4 +- .../tools/javac/6304921/TestLog.java | 8 +- .../javac/diags/DiagnosticGetEndPosition.java | 13 +- .../javac/failover/CheckAttributedTree.java | 11 +- .../javac/parser/DeclarationEndPositions.java | 2 +- .../javac/parser/ReversedSourcePositions.java | 2 +- .../javac/parser/extend/TrialParser.java | 8 +- .../parser/extend/TrialParserFactory.java | 9 +- .../javac/tree/MissingSemicolonTest.java | 4 +- .../tools/javac/tree/TreePosTest.java | 11 +- 32 files changed, 132 insertions(+), 565 deletions(-) delete mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java delete mode 100644 src/jdk.compiler/share/classes/com/sun/tools/javac/util/IntHashTable.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java index 65ce640ef76..bc597876778 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java @@ -91,6 +91,8 @@ public class JavacTaskImpl extends BasicJavacTask { @Override @DefinedBy(Api.COMPILER) public Boolean call() { + if (used.get()) + throw new IllegalStateException(); return doCall().isOK(); } @@ -207,7 +209,6 @@ public class JavacTaskImpl extends BasicJavacTask { // init JavaCompiler and queues compiler = JavaCompiler.instance(context); compiler.keepComments = true; - compiler.genEndPos = true; notYetEntered = new HashMap<>(); if (forParse) { compiler.initProcessAnnotations(processors, args.getFileObjects(), args.getClassNames()); @@ -245,6 +246,8 @@ public class JavacTaskImpl extends BasicJavacTask { @Override @DefinedBy(Api.COMPILER_TREE) public Iterable parse() { + if (used.get()) + throw new IllegalStateException(); Pair, Throwable> result = invocationHelper(this::parseInternal); if (result.snd == null) { return result.fst; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java index 22ee2393a02..6bc5d358b6f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java @@ -114,7 +114,6 @@ import com.sun.tools.javac.tree.DCTree.DCParam; import com.sun.tools.javac.tree.DCTree.DCReference; import com.sun.tools.javac.tree.DocCommentTable; import com.sun.tools.javac.tree.DocTreeMaker; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCCatch; @@ -240,8 +239,7 @@ public class JavacTrees extends DocTrees { @Override @DefinedBy(Api.COMPILER_TREE) public long getEndPosition(CompilationUnitTree file, Tree tree) { - EndPosTable endPosTable = ((JCCompilationUnit) file).endPositions; - return TreeInfo.getEndPos((JCTree) tree, endPosTable); + return TreeInfo.getEndPos((JCTree) tree); } @Override @DefinedBy(Api.COMPILER_TREE) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java index 06caa70d478..4f33bb92b41 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/LintMapper.java @@ -35,7 +35,6 @@ import java.util.function.Consumer; import javax.tools.JavaFileObject; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.TreeInfo; @@ -134,9 +133,9 @@ public class LintMapper { * @param sourceFile source file * @param tree top-level declaration (class, package, or module) */ - public void calculateLints(JavaFileObject sourceFile, JCTree tree, EndPosTable endPositions) { + public void calculateLints(JavaFileObject sourceFile, JCTree tree) { Assert.check(rootLint != null); - fileInfoMap.get(sourceFile).afterAttr(tree, endPositions); + fileInfoMap.get(sourceFile).afterAttr(tree); } /** @@ -184,15 +183,15 @@ public class LintMapper { rootRange = new LintRange(rootLint); for (JCTree decl : tree.defs) { if (isTopLevelDecl(decl)) - unmappedDecls.add(new Span(decl, tree.endPositions)); + unmappedDecls.add(new Span(decl)); } } // After attribution: Discard the span from "unmappedDecls" and populate the declaration's subtree under "rootRange" - void afterAttr(JCTree tree, EndPosTable endPositions) { + void afterAttr(JCTree tree) { for (Iterator i = unmappedDecls.iterator(); i.hasNext(); ) { if (i.next().contains(tree.pos())) { - rootRange.populateSubtree(tree, endPositions); + rootRange.populateSubtree(tree); i.remove(); return; } @@ -225,8 +224,8 @@ public class LintMapper { static final Span MAXIMAL = new Span(Integer.MIN_VALUE, Integer.MAX_VALUE); - Span(JCTree tree, EndPosTable endPositions) { - this(TreeInfo.getStartPos(tree), TreeInfo.getEndPos(tree, endPositions)); + Span(JCTree tree) { + this(TreeInfo.getStartPos(tree), TreeInfo.getEndPos(tree)); } boolean contains(DiagnosticPosition pos) { @@ -256,8 +255,8 @@ public class LintMapper { } // Create a node representing the given declaration and its corresponding Lint configuration - LintRange(JCTree tree, EndPosTable endPositions, Lint lint) { - this(new Span(tree, endPositions), lint, new LinkedList<>()); + LintRange(JCTree tree, Lint lint) { + this(new Span(tree), lint, new LinkedList<>()); } // Find the most specific node in this tree (including me) that contains the given position, if any @@ -277,7 +276,7 @@ public class LintMapper { // Populate a sparse subtree corresponding to the given nested declaration. // Only when the Lint configuration differs from the parent is a node added. - void populateSubtree(JCTree tree, EndPosTable endPositions) { + void populateSubtree(JCTree tree) { new TreeScanner() { private LintRange currentNode = LintRange.this; @@ -320,7 +319,7 @@ public class LintMapper { // Add a new node here and proceed final LintRange previousNode = currentNode; - currentNode = new LintRange(tree, endPositions, newLint); + currentNode = new LintRange(tree, newLint); previousNode.children.add(currentNode); try { recursor.accept(tree); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 5109dfed22e..cdd88b22510 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -2762,8 +2762,7 @@ public class Attr extends JCTree.Visitor { clazzid1 = make.at(clazz.pos).Select(make.Type(encltype), ((JCIdent) clazzid).name); - EndPosTable endPosTable = this.env.toplevel.endPositions; - endPosTable.storeEnd(clazzid1, clazzid.getEndPosition(endPosTable)); + clazzid1.endpos = clazzid.getEndPosition(); if (clazz.hasTag(ANNOTATED_TYPE)) { JCAnnotatedType annoType = (JCAnnotatedType) clazz; List annos = annoType.annotations; @@ -5314,7 +5313,7 @@ public class Attr extends JCTree.Visitor { annotate.flush(); // Now that this tree is attributed, we can calculate the Lint configuration everywhere within it - lintMapper.calculateLints(env.toplevel.sourcefile, env.tree, env.toplevel.endPositions); + lintMapper.calculateLints(env.toplevel.sourcefile, env.tree); } public void attribPackage(DiagnosticPosition pos, PackageSymbol p) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index 2db3435a382..69161fd682c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -48,7 +48,6 @@ import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.jvm.Target; -import com.sun.tools.javac.tree.EndPosTable; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Flags.BLOCK; @@ -156,10 +155,6 @@ public class Lower extends TreeTranslator { */ Env attrEnv; - /** A hash table mapping syntax trees to their ending source positions. - */ - EndPosTable endPosTable; - /* ************************************************************************ * Global mappings *************************************************************************/ @@ -2059,8 +2054,8 @@ public class Lower extends TreeTranslator { } else { make_at(tree.pos()); T result = super.translate(tree); - if (endPosTable != null && result != tree) { - endPosTable.replaceTree(tree, result); + if (result != null && result != tree) { + result.endpos = tree.endpos; } return result; } @@ -4352,7 +4347,6 @@ public class Lower extends TreeTranslator { try { attrEnv = env; this.make = make; - endPosTable = env.toplevel.endPositions; currentClass = null; currentRestype = null; currentMethodDef = null; @@ -4382,7 +4376,6 @@ public class Lower extends TreeTranslator { // note that recursive invocations of this method fail hard attrEnv = null; this.make = null; - endPosTable = null; currentClass = null; currentRestype = null; currentMethodDef = null; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java index 2cd4142c748..3092d16469d 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/CRTable.java @@ -32,7 +32,6 @@ import com.sun.tools.javac.tree.*; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.List; import com.sun.tools.javac.tree.JCTree.*; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree.JCSwitchExpression; /** This class contains the CharacterRangeTable for some method @@ -57,10 +56,6 @@ implements CRTFlags { */ private Map positions = new HashMap<>(); - /** The object for ending positions stored in the parser. - */ - private EndPosTable endPosTable; - /** The tree of the method this table is intended for. * We should traverse this tree to get source ranges. */ @@ -68,9 +63,8 @@ implements CRTFlags { /** Constructor */ - public CRTable(JCTree.JCMethodDecl tree, EndPosTable endPosTable) { + public CRTable(JCTree.JCMethodDecl tree) { this.methodTree = tree; - this.endPosTable = endPosTable; } /** Create a new CRTEntry and add it to the entries. @@ -584,7 +578,7 @@ implements CRTFlags { */ public int endPos(JCTree tree) { if (tree == null) return Position.NOPOS; - return TreeInfo.getEndPos(tree, endPosTable); + return TreeInfo.getEndPos(tree); } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java index e40a2fbfcea..688ea1bd720 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java @@ -45,7 +45,6 @@ import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.jvm.Code.*; import com.sun.tools.javac.jvm.Items.*; import com.sun.tools.javac.resources.CompilerProperties.Errors; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree.*; import static com.sun.tools.javac.code.Flags.*; @@ -162,11 +161,6 @@ public class Gen extends JCTree.Visitor { */ private int nerrs = 0; - /** An object containing mappings of syntax trees to their - * ending source positions. - */ - EndPosTable endPosTable; - boolean inCondSwitchExpression; Chain switchExpressionTrueChain; Chain switchExpressionFalseChain; @@ -455,7 +449,7 @@ public class Gen extends JCTree.Visitor { JCStatement init = make.at(vdef.pos()). Assignment(sym, vdef.init); initCode.append(init); - endPosTable.replaceTree(vdef, init); + init.endpos = vdef.endpos; initTAs.addAll(getAndRemoveNonFieldTAs(sym)); } else if (sym.getConstValue() == null) { // Initialize class (static) variables only if @@ -463,7 +457,7 @@ public class Gen extends JCTree.Visitor { JCStatement init = make.at(vdef.pos). Assignment(sym, vdef.init); clinitCode.append(init); - endPosTable.replaceTree(vdef, init); + init.endpos = vdef.endpos; clinitTAs.addAll(getAndRemoveNonFieldTAs(sym)); } else { checkStringConstant(vdef.init.pos(), sym.getConstValue()); @@ -1027,8 +1021,7 @@ public class Gen extends JCTree.Visitor { varDebugInfo, stackMap, debugCode, - genCrt ? new CRTable(tree, env.toplevel.endPositions) - : null, + genCrt ? new CRTable(tree) : null, syms, types, poolWriter); @@ -2478,7 +2471,6 @@ public class Gen extends JCTree.Visitor { attrEnv = env; ClassSymbol c = cdef.sym; this.toplevel = env.toplevel; - this.endPosTable = toplevel.endPositions; /* method normalizeDefs() can add references to external classes into the constant pool */ cdef.defs = normalizeDefs(cdef.defs, c); @@ -2508,7 +2500,6 @@ public class Gen extends JCTree.Visitor { attrEnv = null; this.env = null; toplevel = null; - endPosTable = null; nerrs = 0; qualifiedSymbolCache.clear(); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java index 2469dc9e031..94292d9a348 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java @@ -438,8 +438,6 @@ public class JavaCompiler { sourceOutput = options.isSet(PRINTSOURCE); // used to be -s lineDebugInfo = options.isUnset(G_CUSTOM) || options.isSet(G_CUSTOM, "lines"); - genEndPos = options.isSet(XJCOV) || - context.get(DiagnosticListener.class) != null; devVerbose = options.isSet("dev"); processPcks = options.isSet("process.packages"); werrorAny = options.isSet(WERROR) || options.isSet(WERROR_CUSTOM, Option.LINT_CUSTOM_ALL); @@ -504,10 +502,6 @@ public class JavaCompiler { */ public boolean lineDebugInfo; - /** Switch: should we store the ending positions? - */ - public boolean genEndPos; - /** Switch: should we debug ignored exceptions */ protected boolean devVerbose; @@ -655,9 +649,8 @@ public class JavaCompiler { TaskEvent e = new TaskEvent(TaskEvent.Kind.PARSE, filename); taskListener.started(e); keepComments = true; - genEndPos = true; } - Parser parser = parserFactory.newParser(content, keepComments(), genEndPos, + Parser parser = parserFactory.newParser(content, keepComments(), lineDebugInfo, filename.isNameCompatible("module-info", Kind.SOURCE)); tree = parser.parseCompilationUnit(); if (verbose) { @@ -697,10 +690,7 @@ public class JavaCompiler { public JCTree.JCCompilationUnit parse(JavaFileObject filename) { JavaFileObject prev = log.useSource(filename); try { - JCTree.JCCompilationUnit t = parse(filename, readSource(filename)); - if (t.endPositions != null) - log.setEndPosTable(filename, t.endPositions); - return t; + return parse(filename, readSource(filename)); } finally { log.useSource(prev); } @@ -1162,7 +1152,6 @@ public class JavaCompiler { options.put("parameters", "parameters"); reader.saveParameterNames = true; keepComments = true; - genEndPos = true; if (!taskListener.isEmpty()) taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING)); deferredDiagnosticHandler = log.new DeferredDiagnosticHandler(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java index f40cb0fb6b7..babe372e7dc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java @@ -33,7 +33,6 @@ import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; import com.sun.tools.javac.resources.CompilerProperties.Warnings; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.*; @@ -1215,7 +1214,7 @@ public class JavaTokenizer extends UnicodeReader { this.cs = cs; this.pos = new SimpleDiagnosticPosition(pos) { @Override - public int getEndPosition(EndPosTable endPosTable) { + public int getEndPosition() { return endPos; } }; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index d658275d4dc..e78537c10f5 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -110,8 +110,7 @@ public class JavacParser implements Parser { /** The name table. */ private Names names; - /** End position mappings container */ - protected final AbstractEndPosTable endPosTable; + protected int errorEndPos = Position.NOPOS; /** A map associating "other nearby documentation comments" * with the preferred documentation comment for a declaration. */ @@ -167,9 +166,8 @@ public class JavacParser implements Parser { protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, - boolean keepLineMap, - boolean keepEndPositions) { - this(fac, S, keepDocComments, keepLineMap, keepEndPositions, false); + boolean keepLineMap) { + this(fac, S, keepDocComments, keepLineMap, false); } /** Construct a parser from a given scanner, tree factory and log. @@ -179,7 +177,6 @@ public class JavacParser implements Parser { Lexer S, boolean keepDocComments, boolean keepLineMap, - boolean keepEndPositions, boolean parseModuleInfo) { this.S = S; nextToken(); // prime the pump @@ -194,7 +191,6 @@ public class JavacParser implements Parser { this.docComments = newDocCommentTable(keepDocComments, fac); this.keepLineMap = keepLineMap; this.errorTree = F.Erroneous(); - this.endPosTable = newEndPosTable(keepEndPositions); this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source); this.allowRecords = Feature.RECORDS.allowedInSource(source); this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source); @@ -218,19 +214,12 @@ public class JavacParser implements Parser { this.parseModuleInfo = false; this.docComments = parser.docComments; this.errorTree = F.Erroneous(); - this.endPosTable = newEndPosTable(false); this.allowYieldStatement = Feature.SWITCH_EXPRESSION.allowedInSource(source); this.allowRecords = Feature.RECORDS.allowedInSource(source); this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source); updateUnexpectedTopLevelDefinitionStartError(false); } - protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) { - return keepEndPositions - ? new SimpleEndPosTable() - : new MinimalEndPosTable(); - } - protected DocCommentTable newDocCommentTable(boolean keepDocComments, ParserFactory fac) { return keepDocComments ? new LazyDocCommentTable(fac) : null; } @@ -667,7 +656,7 @@ public class JavacParser implements Parser { var src = log.currentSource(); return c.getStyle() == Comment.CommentStyle.JAVADOC_LINE && c.getPos().getStartPosition() == 0 && - src.getLineNumber(pos.getEndPosition(src.getEndPosTable())) == 1; + src.getLineNumber(pos.getEndPosition()) == 1; } /** @@ -681,23 +670,26 @@ public class JavacParser implements Parser { /* -------- source positions ------- */ protected void setErrorEndPos(int errPos) { - endPosTable.setErrorEndPos(errPos); + if (errPos > errorEndPos) { + errorEndPos = errPos; + } } /** * Store ending position for a tree, the value of which is the greater of - * last error position in {@link #endPosTable} and the given ending position. + * {@link #errorEndPos} and the given ending position. * @param tree tree node * @param endpos the ending position to associate with {@code tree} * @return {@code tree} */ protected T storeEnd(T tree, int endpos) { - return endPosTable.storeEnd(tree, endpos); + tree.endpos = Math.max(endpos, errorEndPos); + return tree; } /** * Store current token's ending position for a tree, the value of which - * will be the greater of last error position in {@link #endPosTable} + * will be the greater of {@link #errorEndPos} * and the ending position of the current token. * @param tree tree node */ @@ -707,7 +699,7 @@ public class JavacParser implements Parser { /** * Store current token's ending position for a tree, the value of which - * will be the greater of last error position in {@link #endPosTable} + * will be the greater of {@link #errorEndPos} * and the ending position of the previous token. * @param tree tree node */ @@ -733,7 +725,7 @@ public class JavacParser implements Parser { * @param tree The tree node */ public int getEndPos(JCTree tree) { - return endPosTable.getEndPos(tree); + return tree.endpos; } @@ -1269,7 +1261,7 @@ public class JavacParser implements Parser { JCAnnotation typeAnno = F.at(decl.pos) .TypeAnnotation(decl.annotationType, decl.args); - endPosTable.replaceTree(decl, typeAnno); + typeAnno.endpos = decl.endpos; return typeAnno; }); type = insertAnnotationsToMostInner(type, typeAnnos, false); @@ -1358,7 +1350,7 @@ public class JavacParser implements Parser { } else { JCExpression t = F.at(litBuf.first().getStartPosition()).Literal(TypeTag.CLASS, litBuf.stream().map(lit -> (String)lit.getValue()).collect(Collectors.joining())); - storeEnd(t, litBuf.last().getEndPosition(endPosTable)); + storeEnd(t, litBuf.last().getEndPosition()); opStack.prepend(t); return true; } @@ -1654,7 +1646,7 @@ public class JavacParser implements Parser { } // typeArgs saved for next loop iteration. t = toP(F.at(pos).Select(t, ident())); - if (token.pos <= endPosTable.errorEndPos && + if (token.pos <= errorEndPos && token.kind == MONKEYS_AT) { //error recovery, case like: //int i = expr. @@ -1878,7 +1870,7 @@ public class JavacParser implements Parser { tyannos = typeAnnotationsOpt(); } t = toP(F.at(pos1).Select(t, ident(true))); - if (token.pos <= endPosTable.errorEndPos && + if (token.pos <= errorEndPos && token.kind == MONKEYS_AT) { //error recovery, case like: //int i = expr. @@ -2533,7 +2525,7 @@ public class JavacParser implements Parser { int pos = token.pos; nextToken(); accept(CLASS); - if (token.pos == endPosTable.errorEndPos) { + if (token.pos == errorEndPos) { // error recovery Name name; if (LAX_IDENTIFIER.test(token.kind)) { @@ -2867,7 +2859,7 @@ public class JavacParser implements Parser { // error recovery if (token.pos == lastErrPos) return stats.toList(); - if (token.pos <= endPosTable.errorEndPos) { + if (token.pos <= errorEndPos) { skip(false, true, true, true); lastErrPos = token.pos; } @@ -4045,7 +4037,7 @@ public class JavacParser implements Parser { boolean firstTypeDecl = true; // have we seen a class, enum, or interface declaration yet? boolean isImplicitClass = false; OUTER: while (token.kind != EOF) { - if (token.pos <= endPosTable.errorEndPos) { + if (token.pos <= errorEndPos) { // error recovery skip(firstTypeDecl, false, false, false); if (token.kind == EOF) @@ -4156,7 +4148,6 @@ public class JavacParser implements Parser { toplevel.docComments = docComments; if (keepLineMap) toplevel.lineMap = S.getLineMap(); - toplevel.endPositions = this.endPosTable; return toplevel; } @@ -4576,7 +4567,7 @@ public class JavacParser implements Parser { hasStructuralErrors = true; } defs.append(enumeratorDeclaration(enumName)); - if (token.pos <= endPosTable.errorEndPos) { + if (token.pos <= errorEndPos) { // error recovery skip(false, true, true, false); } else { @@ -4599,7 +4590,7 @@ public class JavacParser implements Parser { wasError = false; defs.appendList(classOrInterfaceOrRecordBodyDeclaration(null, enumName, false, false)); - if (token.pos <= endPosTable.errorEndPos) { + if (token.pos <= errorEndPos) { // error recovery skip(false, true, true, false); } @@ -4696,7 +4687,7 @@ public class JavacParser implements Parser { */ List classInterfaceOrRecordBody(Name className, boolean isInterface, boolean isRecord) { accept(LBRACE); - if (token.pos <= endPosTable.errorEndPos) { + if (token.pos <= errorEndPos) { // error recovery skip(false, true, false, false); if (token.kind == LBRACE) @@ -4707,7 +4698,7 @@ public class JavacParser implements Parser { ListBuffer defs = new ListBuffer<>(); while (token.kind != RBRACE && token.kind != EOF) { defs.appendList(classOrInterfaceOrRecordBodyDeclaration(null, className, isInterface, isRecord)); - if (token.pos <= endPosTable.errorEndPos) { + if (token.pos <= errorEndPos) { // error recovery skip(false, true, true, false); } @@ -5067,7 +5058,7 @@ public class JavacParser implements Parser { boolean unclosedParameterList; if (!isRecord || name != names.init || token.kind == LPAREN) { params = formalParameters(); - unclosedParameterList = token.pos == endPosTable.errorEndPos; + unclosedParameterList = token.pos == errorEndPos; if (!isVoid) type = bracketsOpt(type); if (token.kind == THROWS) { nextToken(); @@ -5093,7 +5084,7 @@ public class JavacParser implements Parser { defaultValue = null; accept(SEMI, tk -> Errors.Expected2(LBRACE, SEMI)); } - if (token.pos <= endPosTable.errorEndPos) { + if (token.pos <= errorEndPos) { // error recovery // look if there is a probable missing opening brace, // and if yes, parse as a block @@ -5631,70 +5622,4 @@ public class JavacParser implements Parser { unexpectedTopLevelDefinitionStartError = Errors.Expected3(CLASS, INTERFACE, ENUM); } } - - /** - * A straightforward {@link EndPosTable} implementation. - */ - protected static class SimpleEndPosTable extends AbstractEndPosTable { - - private final IntHashTable endPosMap = new IntHashTable(); - - @Override - public T storeEnd(T tree, int endpos) { - endPosMap.put(tree, Math.max(endpos, errorEndPos)); - return tree; - } - - @Override - public int getEndPos(JCTree tree) { - int value = endPosMap.get(tree); - // As long as Position.NOPOS==-1, this just returns value. - return (value == -1) ? Position.NOPOS : value; - } - - @Override - public int replaceTree(JCTree oldTree, JCTree newTree) { - int pos = endPosMap.remove(oldTree); - if (pos != -1 && newTree != null) { - storeEnd(newTree, pos); - } - return pos; - } - } - - /** - * A minimal implementation that only stores what's required. - */ - protected static class MinimalEndPosTable extends SimpleEndPosTable { - - @Override - public T storeEnd(T tree, int endpos) { - switch (tree.getTag()) { - case MODULEDEF: - case PACKAGEDEF: - case CLASSDEF: - case METHODDEF: - case VARDEF: - break; - default: - return tree; - } - return super.storeEnd(tree, endpos); - } - } - - protected abstract static class AbstractEndPosTable implements EndPosTable { - - /** - * Store the last error position. - */ - public int errorEndPos = Position.NOPOS; - - @Override - public void setErrorEndPos(int errPos) { - if (errPos > errorEndPos) { - errorEndPos = errPos; - } - } - } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java index f9e187315ba..d06dd2cacda 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/ParserFactory.java @@ -28,7 +28,6 @@ package com.sun.tools.javac.parser; import java.util.Locale; import com.sun.tools.javac.api.JavacTrees; -import com.sun.tools.javac.code.Lint; import com.sun.tools.javac.code.Preview; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.tree.DocTreeMaker; @@ -89,13 +88,9 @@ public class ParserFactory { this.trees = JavacTrees.instance(context); } - public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) { - return newParser(input, keepDocComments, keepEndPos, keepLineMap, false); - } - - public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) { + public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepLineMap, boolean parseModuleInfo) { Lexer lexer = scannerFactory.newScanner(input, keepDocComments); - return new JavacParser(this, lexer, keepDocComments, keepLineMap, keepEndPos, parseModuleInfo); + return new JavacParser(this, lexer, keepDocComments, keepLineMap, parseModuleInfo); } public JavacTrees getTrees() { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java index a999786f119..023e5c74b3a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java @@ -305,7 +305,7 @@ public abstract class DCTree implements DocTree { } @Override - public int getEndPosition(EndPosTable endPosTable) { + public int getEndPosition() { return comment.getSourcePos(end); } }; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java deleted file mode 100644 index 83fe402c0a7..00000000000 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/EndPosTable.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2011, 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. - */ - -package com.sun.tools.javac.tree; - -import com.sun.tools.javac.util.Position; - -/** - * Specifies the methods to access a mappings of syntax trees to end positions. - * - *

      - * Implementations must store end positions for at least these node types: - *

        - *
      • {@link JCTree.JCModuleDecl} - *
      • {@link JCTree.JCPackageDecl} - *
      • {@link JCTree.JCClassDecl} - *
      • {@link JCTree.JCMethodDecl} - *
      • {@link JCTree.JCVariableDecl} - *
      - * - *

      This is NOT part of any supported API. - * If you write code that depends on this, you do so at your own - * risk. This code and its internal interfaces are subject to change - * or deletion without notice.

      - */ -public interface EndPosTable { - - /** - * This method will return the end position of a given tree, otherwise a - * Positions.NOPOS will be returned. - * @param tree JCTree - * @return position of the source tree or Positions.NOPOS for non-existent mapping - */ - int getEndPos(JCTree tree); - - /** - * Store ending position for a tree, the value of which is the greater of - * last error position and the given ending position. - * @param tree The tree. - * @param endpos The ending position to associate with the tree. - * @return the {@code tree} - */ - T storeEnd(T tree, int endpos); - - /** - * Set the error position during the parsing phases, the value of which - * will be set only if it is greater than the last stored error position. - * @param errPos The error position - */ - void setErrorEndPos(int errPos); - - /** - * Give an old tree and a new tree, the old tree will be replaced with - * the new tree, the position of the new tree will be that of the old - * tree. - * @param oldtree a JCTree to be replaced - * @param newtree a JCTree to be replaced with, or null to just remove {@code oldtree} - * @return position of the old tree or Positions.NOPOS for non-existent mapping - */ - int replaceTree(JCTree oldtree, JCTree newtree); -} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java index 6501fd5d96c..e0a99a6f103 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java @@ -431,6 +431,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { */ public int pos; + /* The (encoded) end position in the source file. @see util.Position. + */ + public int endpos = Position.NOPOS; + /* The type of this node. */ public Type type; @@ -514,8 +518,8 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { } // for default DiagnosticPosition - public int getEndPosition(EndPosTable endPosTable) { - return noNoPos(TreeInfo.getEndPos(this, endPosTable)); + public int getEndPosition() { + return noNoPos(TreeInfo.getEndPos(this)); } private int noNoPos(int position) { @@ -552,9 +556,6 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition { /** A table that stores all documentation comments indexed by the tree * nodes they refer to. defined only if option -s is set. */ public DocCommentTable docComments = null; - /* An object encapsulating ending positions of source ranges indexed by - * the tree nodes they belong to. Defined only if option -Xjcov is set. */ - public EndPosTable endPositions = null; protected JCCompilationUnit(List defs) { this.defs = defs; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 3f73bfd2296..aa616f3f580 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -31,7 +31,6 @@ import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.RecordComponent; -import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*; @@ -647,18 +646,13 @@ public class TreeInfo { /** The end position of given tree, given a table of end positions generated by the parser */ - public static int getEndPos(JCTree tree, EndPosTable endPosTable) { + public static int getEndPos(JCTree tree) { if (tree == null) return Position.NOPOS; - if (endPosTable == null) { - // fall back on limited info in the tree - return endPos(tree); - } - - int mapPos = endPosTable.getEndPos(tree); - if (mapPos != Position.NOPOS) - return mapPos; + int endpos = tree.endpos; + if (endpos != Position.NOPOS) + return endpos; switch(tree.getTag()) { case BITOR_ASG: case BITXOR_ASG: case BITAND_ASG: @@ -678,57 +672,57 @@ public class TreeInfo { case COMPL: case PREINC: case PREDEC: - return getEndPos(((JCOperatorExpression) tree).getOperand(RIGHT), endPosTable); + return getEndPos(((JCOperatorExpression) tree).getOperand(RIGHT)); case CASE: - return getEndPos(((JCCase) tree).stats.last(), endPosTable); + return getEndPos(((JCCase) tree).stats.last()); case CATCH: - return getEndPos(((JCCatch) tree).body, endPosTable); + return getEndPos(((JCCatch) tree).body); case CONDEXPR: - return getEndPos(((JCConditional) tree).falsepart, endPosTable); + return getEndPos(((JCConditional) tree).falsepart); case FORLOOP: - return getEndPos(((JCForLoop) tree).body, endPosTable); + return getEndPos(((JCForLoop) tree).body); case FOREACHLOOP: - return getEndPos(((JCEnhancedForLoop) tree).body, endPosTable); + return getEndPos(((JCEnhancedForLoop) tree).body); case IF: { JCIf node = (JCIf)tree; if (node.elsepart == null) { - return getEndPos(node.thenpart, endPosTable); + return getEndPos(node.thenpart); } else { - return getEndPos(node.elsepart, endPosTable); + return getEndPos(node.elsepart); } } case LABELLED: - return getEndPos(((JCLabeledStatement) tree).body, endPosTable); + return getEndPos(((JCLabeledStatement) tree).body); case MODIFIERS: - return getEndPos(((JCModifiers) tree).annotations.last(), endPosTable); + return getEndPos(((JCModifiers) tree).annotations.last()); case SYNCHRONIZED: - return getEndPos(((JCSynchronized) tree).body, endPosTable); + return getEndPos(((JCSynchronized) tree).body); case TOPLEVEL: - return getEndPos(((JCCompilationUnit) tree).defs.last(), endPosTable); + return getEndPos(((JCCompilationUnit) tree).defs.last()); case TRY: { JCTry node = (JCTry)tree; if (node.finalizer != null) { - return getEndPos(node.finalizer, endPosTable); + return getEndPos(node.finalizer); } else if (!node.catchers.isEmpty()) { - return getEndPos(node.catchers.last(), endPosTable); + return getEndPos(node.catchers.last()); } else { - return getEndPos(node.body, endPosTable); + return getEndPos(node.body); } } case WILDCARD: - return getEndPos(((JCWildcard) tree).inner, endPosTable); + return getEndPos(((JCWildcard) tree).inner); case TYPECAST: - return getEndPos(((JCTypeCast) tree).expr, endPosTable); + return getEndPos(((JCTypeCast) tree).expr); case TYPETEST: - return getEndPos(((JCInstanceOf) tree).pattern, endPosTable); + return getEndPos(((JCInstanceOf) tree).pattern); case WHILELOOP: - return getEndPos(((JCWhileLoop) tree).body, endPosTable); + return getEndPos(((JCWhileLoop) tree).body); case ANNOTATED_TYPE: - return getEndPos(((JCAnnotatedType) tree).underlyingType, endPosTable); + return getEndPos(((JCAnnotatedType) tree).underlyingType); case ERRONEOUS: { JCErroneous node = (JCErroneous)tree; if (node.errs != null && node.errs.nonEmpty()) - return getEndPos(node.errs.last(), endPosTable); + return getEndPos(node.errs.last()); } } return Position.NOPOS; @@ -745,8 +739,8 @@ public class TreeInfo { public JCTree getTree() { return tree; } public int getStartPosition() { return TreeInfo.getStartPos(tree); } public int getPreferredPosition() { return endPos; } - public int getEndPosition(EndPosTable endPosTable) { - return TreeInfo.getEndPos(tree, endPosTable); + public int getEndPosition() { + return TreeInfo.getEndPos(tree); } }; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/DiagnosticSource.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/DiagnosticSource.java index baae3101811..3339900733f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/DiagnosticSource.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/DiagnosticSource.java @@ -31,7 +31,6 @@ import java.nio.CharBuffer; import javax.tools.JavaFileObject; import com.sun.tools.javac.file.JavacFileManager; -import com.sun.tools.javac.tree.EndPosTable; import static com.sun.tools.javac.util.LayoutCharacters.*; @@ -127,16 +126,6 @@ public class DiagnosticSource { } } - public EndPosTable getEndPosTable() { - return endPosTable; - } - - public void setEndPosTable(EndPosTable t) { - if (endPosTable != null && endPosTable != t) - throw new IllegalStateException("endPosTable already set"); - endPosTable = t; - } - /** Find the line in the buffer that contains the current position * @param pos Character offset into the buffer */ @@ -197,8 +186,6 @@ public class DiagnosticSource { /** The underlying file object. */ protected JavaFileObject fileObject; - protected EndPosTable endPosTable; - /** A soft reference to the content of the file object. */ protected SoftReference refBuf; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/IntHashTable.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/IntHashTable.java deleted file mode 100644 index 409dc703d60..00000000000 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/IntHashTable.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.tools.javac.util; - -/** - * A hash table that maps Object to int. - * - * This is a custom hash table optimised for the Object {@literal ->} int - * maps. This is done to avoid unnecessary object allocation in the image set. - * - * @author Charles Turner - * @author Per Bothner - */ -public class IntHashTable { - private static final int DEFAULT_INITIAL_SIZE = 64; - protected Object[] objs; // the domain set - protected int[] ints; // the image set - protected int mask; // used to clip int's into the domain - protected int num_bindings; // the number of mappings (including DELETED) - private static final Object DELETED = new Object(); - - /** - * Construct an Object {@literal ->} int hash table. - * - * The default size of the hash table is 64 mappings. - */ - public IntHashTable() { - objs = new Object[DEFAULT_INITIAL_SIZE]; - ints = new int[DEFAULT_INITIAL_SIZE]; - mask = DEFAULT_INITIAL_SIZE - 1; - } - - /** - * Construct an Object {@literal ->} int hash table with a specified amount of mappings. - * @param capacity The number of default mappings in this hash table. - */ - public IntHashTable(int capacity) { - int log2Size = 4; - while (capacity > (1 << log2Size)) { - log2Size++; - } - capacity = 1 << log2Size; - objs = new Object[capacity]; - ints = new int[capacity]; - mask = capacity - 1; - } - - /** - * Compute the hash code of a given object. - * - * @param key The object whose hash code is to be computed. - * @return zero if the object is null, otherwise the identityHashCode - */ - protected int hash(Object key) { - return System.identityHashCode(key); - } - - /** - * Find either the index of a key's value, or the index of an available space. - * - * @param key The key to whose index you want to find. - * @return Either the index of the key's value, or an index pointing to - * unoccupied space. - */ - protected int lookup(Object key) { - Object node; - int hash = hash(key); - int hash1 = hash ^ (hash >>> 15); - int hash2 = (hash ^ (hash << 6)) | 1; //ensure coprimeness - int deleted = -1; - for (int i = hash1 & mask;; i = (i + hash2) & mask) { - node = objs[i]; - if (node == key) - return i; - if (node == null) - return deleted >= 0 ? deleted : i; - if (node == DELETED && deleted < 0) - deleted = i; - } - } - - /** - * Return the value to which the specified key is mapped. - * - * @param key The key to whose value you want to find. - * @return A non-negative integer if the value is found. - * Otherwise, it is -1. - */ - public int get(Object key) { - int index = lookup(key); - Object node = objs[index]; - return node == null || node == DELETED ? -1 : ints[index]; - } - - /** - * Associates the specified key with the specified value in this map. - * - * @param key key with which the specified value is to be associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or -1 if there was - * no mapping for key. - */ - public int put(Object key, int value) { - int index = lookup(key); - Object old = objs[index]; - if (old == null || old == DELETED) { - objs[index] = key; - ints[index] = value; - if (old != DELETED) - num_bindings++; - if (3 * num_bindings >= 2 * objs.length) - rehash(); - return -1; - } else { // update existing mapping - int oldValue = ints[index]; - ints[index] = value; - return oldValue; - } - } - - /** - * Remove the mapping(key and value) of the specified key. - * - * @param key the key to whose value you want to remove. - * @return the removed value associated with the specified key, - * or -1 if there was no mapping for the specified key. - */ - public int remove(Object key) { - int index = lookup(key); - Object old = objs[index]; - if (old == null || old == DELETED) - return -1; - objs[index] = DELETED; - return ints[index]; - } - - /** - * Expand the hash table when it exceeds the load factor. - * - * Rehash the existing objects. - */ - protected void rehash() { - Object[] oldObjsTable = objs; - int[] oldIntsTable = ints; - int newCapacity = oldObjsTable.length << 1; - objs = new Object[newCapacity]; - ints = new int[newCapacity]; - mask = newCapacity - 1; - num_bindings = 0; // this is recomputed below - Object key; - for (int i = oldIntsTable.length; --i >= 0;) { - key = oldObjsTable[i]; - if (key != null && key != DELETED) - put(key, oldIntsTable[i]); - } - } - - /** - * Removes all mappings from this map. - */ - public void clear() { - for (int i = objs.length; --i >= 0;) { - objs[i] = null; - } - num_bindings = 0; - } -} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java index c9f529eae55..328183c0cb3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -38,7 +38,6 @@ import javax.tools.JavaFileObject; import com.sun.tools.javac.api.DiagnosticFormatter; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.DefinedBy.Api; @@ -364,10 +363,8 @@ public class JCDiagnostic implements Diagnostic { /** Get the position within the file that most accurately defines the * location for the diagnostic. */ int getPreferredPosition(); - /** If there is a tree node, and if endPositions are available, get - * the end position of the tree node. Otherwise, just returns the - * same as getPreferredPosition(). */ - int getEndPosition(EndPosTable endPosTable); + /** If there is a tree node, get the end position of the tree node. */ + int getEndPosition(); /** Get the position that determines which Lint configuration applies. */ default int getLintPosition() { return getStartPosition(); @@ -389,8 +386,8 @@ public class JCDiagnostic implements Diagnostic { return orig.getPreferredPosition(); } @Override - public int getEndPosition(EndPosTable endPosTable) { - return orig.getEndPosition(endPosTable); + public int getEndPosition() { + return orig.getEndPosition(); } @Override public int getLintPosition() { @@ -421,7 +418,7 @@ public class JCDiagnostic implements Diagnostic { return pos; } - public int getEndPosition(EndPosTable endPosTable) { + public int getEndPosition() { return pos; } @@ -747,7 +744,7 @@ public class JCDiagnostic implements Diagnostic { } protected int getIntEndPosition() { - return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable())); + return (position == null ? Position.NOPOS : position.getEndPosition()); } @DefinedBy(Api.COMPILER) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java index a4109a35ccb..b061d2283a0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Log.java @@ -55,7 +55,6 @@ import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.main.Main; import com.sun.tools.javac.main.Option; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.TreeInfo; @@ -594,11 +593,6 @@ public class Log extends AbstractLog { return diagListener != null; } - public void setEndPosTable(JavaFileObject name, EndPosTable endPosTable) { - Assert.checkNonNull(name); - getSource(name).setEndPosTable(endPosTable); - } - /** Return current sourcefile. */ public JavaFileObject currentSourceFile() { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocLog.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocLog.java index 84c32a4c732..b3bf1c14cb1 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocLog.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocLog.java @@ -55,7 +55,6 @@ import com.sun.source.doctree.TextTree; import com.sun.tools.javac.tree.DCTree.DCDocComment; import com.sun.tools.javac.tree.DCTree; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.util.Context.Factory; import com.sun.tools.javac.util.DiagnosticSource; import com.sun.source.tree.CompilationUnitTree; @@ -616,7 +615,7 @@ public class JavadocLog extends Log implements Reporter { } @Override - public int getEndPosition(EndPosTable endPosTable) { + public int getEndPosition() { return end; } }; diff --git a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java index 0219fa0eaf8..11fe05fbb98 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParser.java @@ -75,9 +75,8 @@ class ReplParser extends JavacParser { com.sun.tools.javac.parser.Lexer S, boolean keepDocComments, boolean keepLineMap, - boolean keepEndPositions, boolean forceExpression) { - super(fac, S, keepDocComments, keepLineMap, keepEndPositions); + super(fac, S, keepDocComments, keepLineMap); this.forceExpression = forceExpression; this.source = fac.source; } @@ -103,7 +102,7 @@ class ReplParser extends JavacParser { boolean firstTypeDecl = true; while (token.kind != EOF) { - if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) { + if (token.pos > 0 && token.pos <= errorEndPos) { // error recovery skip(true, false, false, false); if (token.kind == EOF) { @@ -141,7 +140,6 @@ class ReplParser extends JavacParser { storeEnd(toplevel, S.prevToken().endPos); } toplevel.lineMap = S.getLineMap(); - toplevel.endPositions = this.endPosTable; return toplevel; } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java b/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java index 5c02561807b..3a3b25dade0 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/ReplParserFactory.java @@ -60,13 +60,8 @@ class ReplParserFactory extends ParserFactory { } @Override - public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) { + public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepLineMap, boolean parseModuleInfo) { com.sun.tools.javac.parser.Lexer lexer = scannerFactory.newScanner(input, keepDocComments); - return new ReplParser(this, lexer, keepDocComments, keepLineMap, keepEndPos, forceExpression); - } - - @Override - public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) { - return newParser(input, keepDocComments, keepEndPos, keepLineMap); + return new ReplParser(this, lexer, keepDocComments, keepLineMap, forceExpression); } } diff --git a/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java b/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java index 13c07ea32c0..0de0e27ec07 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java @@ -819,9 +819,9 @@ class TaskFactory { } @Override - public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) { + public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepLineMap, boolean parseModuleInfo) { com.sun.tools.javac.parser.Lexer lexer = scannerFactory.newScanner(input, keepDocComments); - return new JavacParser(this, lexer, keepDocComments, keepLineMap, keepEndPos, parseModuleInfo) { + return new JavacParser(this, lexer, keepDocComments, keepLineMap, parseModuleInfo) { @Override public JCExpression parseType(boolean allowVar, com.sun.tools.javac.util.List annotations) { int pos = token.pos; diff --git a/test/langtools/tools/javac/6304921/TestLog.java b/test/langtools/tools/javac/6304921/TestLog.java index 8f00a14b9e2..9d0219accf1 100644 --- a/test/langtools/tools/javac/6304921/TestLog.java +++ b/test/langtools/tools/javac/6304921/TestLog.java @@ -42,7 +42,6 @@ import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.parser.Parser; import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.resources.CompilerProperties.Warnings; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.Context; @@ -96,9 +95,8 @@ public class TestLog CharSequence cs = fo.getCharContent(true); Parser parser = pfac.newParser(cs, false, genEndPos, false); JCTree.JCCompilationUnit tree = parser.parseCompilationUnit(); - log.setEndPosTable(fo, tree.endPositions); - TreeScanner ts = new LogTester(log, tree.endPositions); + TreeScanner ts = new LogTester(log); ts.scan(tree); check(log.nerrors, 4, "errors"); @@ -117,9 +115,8 @@ public class TestLog } private static class LogTester extends TreeScanner { - LogTester(Log log, EndPosTable endPosTable) { + LogTester(Log log) { this.log = log; - this.endPosTable = endPosTable; } public void visitIf(JCTree.JCIf tree) { @@ -138,7 +135,6 @@ public class TestLog } private Log log; - private EndPosTable endPosTable; } private static class StringJavaFileObject extends SimpleJavaFileObject { diff --git a/test/langtools/tools/javac/diags/DiagnosticGetEndPosition.java b/test/langtools/tools/javac/diags/DiagnosticGetEndPosition.java index 1835cd46167..160f358f32f 100644 --- a/test/langtools/tools/javac/diags/DiagnosticGetEndPosition.java +++ b/test/langtools/tools/javac/diags/DiagnosticGetEndPosition.java @@ -118,9 +118,16 @@ public class DiagnosticGetEndPosition { compiler.getTask( null, null, - d -> assertEquals("", //ideally would be "0", but the positions are not fully set yet - implCode.substring((int) d.getStartPosition(), - (int) d.getEndPosition())), + d -> { + // Use line and column instead of start and end positions + // to handle platform-dependent newlines, which could be + // different between the text block and the written file. + int line = (int) d.getLineNumber(); + int col = (int) d.getColumnNumber(); + assertEquals(1, d.getEndPosition() - d.getStartPosition()); + String substring = implCode.split("\\R")[line - 1].substring(col - 1, col); + assertEquals("0", substring); + }, List.of("-sourcepath", src.toString(), "-Xlint:divzero"), null, fm.getJavaFileObjects(src.resolve("test").resolve("Test.java")) diff --git a/test/langtools/tools/javac/failover/CheckAttributedTree.java b/test/langtools/tools/javac/failover/CheckAttributedTree.java index 9b28d2bebd4..050a302bc6e 100644 --- a/test/langtools/tools/javac/failover/CheckAttributedTree.java +++ b/test/langtools/tools/javac/failover/CheckAttributedTree.java @@ -92,7 +92,6 @@ import com.sun.source.util.TaskEvent.Kind; import com.sun.source.util.TaskListener; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCBreak; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; @@ -367,8 +366,7 @@ public class CheckAttributedTree { private class NPETester extends TreeScanner { void test(JCCompilationUnit cut, JCTree tree) { sourcefile = cut.sourcefile; - endPosTable = cut.endPositions; - encl = new Info(tree, endPosTable); + encl = new Info(tree); tree.accept(this); } @@ -379,7 +377,7 @@ public class CheckAttributedTree { return; } - Info self = new Info(tree, endPosTable); + Info self = new Info(tree); if (mandatoryType(tree)) { check(tree.type != null, "'null' field 'type' found in tree ", self); @@ -454,7 +452,6 @@ public class CheckAttributedTree { } JavaFileObject sourcefile; - EndPosTable endPosTable; Info encl; } } @@ -498,12 +495,12 @@ public class CheckAttributedTree { end = Integer.MAX_VALUE; } - Info(JCTree tree, EndPosTable endPosTable) { + Info(JCTree tree) { this.tree = tree; tag = tree.getTag(); start = TreeInfo.getStartPos(tree); pos = tree.pos; - end = TreeInfo.getEndPos(tree, endPosTable); + end = TreeInfo.getEndPos(tree); } @Override diff --git a/test/langtools/tools/javac/parser/DeclarationEndPositions.java b/test/langtools/tools/javac/parser/DeclarationEndPositions.java index 226b77769d1..c61a92e80cd 100644 --- a/test/langtools/tools/javac/parser/DeclarationEndPositions.java +++ b/test/langtools/tools/javac/parser/DeclarationEndPositions.java @@ -78,7 +78,7 @@ public class DeclarationEndPositions { throw new AssertionError(String.format( "wrong %s pos %d for \"%s\" in \"%s\"", "start", start, tree, input)); } - int end = TreeInfo.getEndPos(tree, unit.endPositions); + int end = TreeInfo.getEndPos(tree); if (markers.charAt(end - 1) != '>') { throw new AssertionError(String.format( "wrong %s pos %d for \"%s\" in \"%s\"", "end", end, tree, input)); diff --git a/test/langtools/tools/javac/parser/ReversedSourcePositions.java b/test/langtools/tools/javac/parser/ReversedSourcePositions.java index d092d23d8c1..6596430dca5 100644 --- a/test/langtools/tools/javac/parser/ReversedSourcePositions.java +++ b/test/langtools/tools/javac/parser/ReversedSourcePositions.java @@ -72,7 +72,7 @@ public class ReversedSourcePositions { public Void scan(Tree node, Void aVoid) { if (node instanceof JCTree tree) { int start = tree.getStartPosition(); - int end = tree.getEndPosition(unit.endPositions); + int end = tree.getEndPosition(); if (start >= end) { throw new AssertionError( String.format("[%d, %d] %s %s\n", start, end, tree.getKind(), tree)); diff --git a/test/langtools/tools/javac/parser/extend/TrialParser.java b/test/langtools/tools/javac/parser/extend/TrialParser.java index c9a858fbc47..ca4e3995bc3 100644 --- a/test/langtools/tools/javac/parser/extend/TrialParser.java +++ b/test/langtools/tools/javac/parser/extend/TrialParser.java @@ -66,9 +66,8 @@ class TrialParser extends JavacParser { public TrialParser(ParserFactory fac, com.sun.tools.javac.parser.Lexer S, boolean keepDocComments, - boolean keepLineMap, - boolean keepEndPositions) { - super(fac, S, keepDocComments, keepLineMap, keepEndPositions); + boolean keepLineMap) { + super(fac, S, keepDocComments, keepLineMap); } @Override @@ -102,7 +101,7 @@ class TrialParser extends JavacParser { boolean firstTypeDecl = true; while (token.kind != EOF) { - if (token.pos > 0 && token.pos <= endPosTable.errorEndPos) { + if (token.pos > 0 && token.pos <= errorEndPos) { // error recovery skip(true, false, false, false); if (token.kind == EOF) { @@ -139,7 +138,6 @@ class TrialParser extends JavacParser { storeEnd(toplevel, S.prevToken().endPos); } toplevel.lineMap = S.getLineMap(); - toplevel.endPositions = this.endPosTable; return toplevel; } diff --git a/test/langtools/tools/javac/parser/extend/TrialParserFactory.java b/test/langtools/tools/javac/parser/extend/TrialParserFactory.java index 6ac4c60d279..6f62175c82a 100644 --- a/test/langtools/tools/javac/parser/extend/TrialParserFactory.java +++ b/test/langtools/tools/javac/parser/extend/TrialParserFactory.java @@ -48,13 +48,8 @@ class TrialParserFactory extends ParserFactory { } @Override - public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) { + public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepLineMap, boolean parseModuleInfo) { com.sun.tools.javac.parser.Lexer lexer = scannerFactory.newScanner(input, keepDocComments); - return new TrialParser(this, lexer, keepDocComments, keepLineMap, keepEndPos); - } - - @Override - public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) { - return newParser(input, keepDocComments, keepEndPos, keepLineMap); + return new TrialParser(this, lexer, keepDocComments, keepLineMap); } } diff --git a/test/langtools/tools/javac/tree/MissingSemicolonTest.java b/test/langtools/tools/javac/tree/MissingSemicolonTest.java index 2c70c4841be..cedcf1e5f98 100644 --- a/test/langtools/tools/javac/tree/MissingSemicolonTest.java +++ b/test/langtools/tools/javac/tree/MissingSemicolonTest.java @@ -115,7 +115,7 @@ public class MissingSemicolonTest { public Void scan(Tree tree, Void p) { if (tree != null) { int start = ((JCTree) tree).getStartPosition(); - int end = ((JCTree) tree).getEndPosition(unit.endPositions); + int end = ((JCTree) tree).getEndPosition(); spans.add(new int[] {start, end}); } @@ -134,7 +134,7 @@ public class MissingSemicolonTest { public Void scan(Tree tree, Void p) { if (tree != null) { int start = ((JCTree) tree).getStartPosition(); - int end = ((JCTree) tree).getEndPosition(updated.endPositions); + int end = ((JCTree) tree).getEndPosition(); if (tree.getKind() != Kind.ERRONEOUS) { int[] expected = nextSpan.next(); diff --git a/test/langtools/tools/javac/tree/TreePosTest.java b/test/langtools/tools/javac/tree/TreePosTest.java index 9e6dcf61306..ecc0d477cdb 100644 --- a/test/langtools/tools/javac/tree/TreePosTest.java +++ b/test/langtools/tools/javac/tree/TreePosTest.java @@ -69,7 +69,6 @@ import com.sun.source.tree.CompilationUnitTree; import com.sun.tools.javac.api.JavacTaskPool; import com.sun.tools.javac.api.JavacTool; import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotatedType; import com.sun.tools.javac.tree.JCTree.JCCase; @@ -347,7 +346,6 @@ public class TreePosTest { private boolean compactSourceFile; void test(JCCompilationUnit tree) { sourcefile = tree.sourcefile; - endPosTable = tree.endPositions; encl = new Info(); List nonImports = tree.defs .stream() @@ -355,7 +353,7 @@ public class TreePosTest { .toList(); compactSourceFile = nonImports.size() == 1 && nonImports.get(0) instanceof JCClassDecl classDecl && - tree.endPositions.getEndPos(classDecl) == NOPOS; + classDecl.endpos == NOPOS; tree.accept(this); } @@ -364,7 +362,7 @@ public class TreePosTest { if (tree == null) return; - Info self = new Info(tree, endPosTable); + Info self = new Info(tree); if (check(encl, self)) { // Modifiers nodes are present throughout the tree even where // there is no corresponding source text. @@ -504,7 +502,6 @@ public class TreePosTest { } JavaFileObject sourcefile; - EndPosTable endPosTable; Info encl; } @@ -521,12 +518,12 @@ public class TreePosTest { end = Integer.MAX_VALUE; } - Info(JCTree tree, EndPosTable endPosTable) { + Info(JCTree tree) { this.tree = tree; tag = tree.getTag(); start = TreeInfo.getStartPos(tree); pos = tree.pos; - end = TreeInfo.getEndPos(tree, endPosTable); + end = TreeInfo.getEndPos(tree); } @Override From 3f3dcb708d2e8326c96c42566fa765a878e68bf6 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Wed, 4 Feb 2026 08:41:38 +0000 Subject: [PATCH 116/215] 8376810: Make Atomic default constructor non-explicit Reviewed-by: kbarrett, aboldtch, azafari, tschatzl --- src/hotspot/share/runtime/atomic.hpp | 14 ++-- test/hotspot/gtest/runtime/test_atomic.cpp | 87 +++++++++++++++++++++- 2 files changed, 95 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/runtime/atomic.hpp b/src/hotspot/share/runtime/atomic.hpp index f708e9c18ca..894e04eec57 100644 --- a/src/hotspot/share/runtime/atomic.hpp +++ b/src/hotspot/share/runtime/atomic.hpp @@ -58,9 +58,10 @@ // ValueType -> T // // special functions: -// explicit constructor(T) +// constexpr constructor() // See (2) below +// explicit constexpr constructor(T) // noncopyable -// destructor +// destructor // Trivial // // static member functions: // value_offset_in_bytes() -> int // constexpr @@ -343,7 +344,8 @@ class AtomicImpl::Atomic : public SupportsArithmetic { public: - explicit constexpr Atomic(T value = 0) : SupportsArithmetic(value) {} + constexpr Atomic() : Atomic(0) {} + explicit constexpr Atomic(T value) : SupportsArithmetic(value) {} NONCOPYABLE(Atomic); @@ -383,7 +385,8 @@ class AtomicImpl::Atomic : public CommonCore { public: - explicit constexpr Atomic(T value = 0) : CommonCore(value) {} + constexpr Atomic() : Atomic(0) {} + explicit constexpr Atomic(T value) : CommonCore(value) {} NONCOPYABLE(Atomic); @@ -399,7 +402,8 @@ class AtomicImpl::Atomic : public SupportsArithmetic { public: - explicit constexpr Atomic(T value = nullptr) : SupportsArithmetic(value) {} + constexpr Atomic() : Atomic(nullptr) {} + explicit constexpr Atomic(T value) : SupportsArithmetic(value) {} NONCOPYABLE(Atomic); diff --git a/test/hotspot/gtest/runtime/test_atomic.cpp b/test/hotspot/gtest/runtime/test_atomic.cpp index 753dde0ca57..80e67ef0bf9 100644 --- a/test/hotspot/gtest/runtime/test_atomic.cpp +++ b/test/hotspot/gtest/runtime/test_atomic.cpp @@ -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,15 +22,100 @@ * */ +#include "cppstdlib/new.hpp" #include "cppstdlib/type_traits.hpp" #include "metaprogramming/primitiveConversions.hpp" #include "runtime/atomic.hpp" +#include "runtime/os.hpp" #include "unittest.hpp" // These tests of Atomic only verify functionality. They don't verify // atomicity. +template +struct AtomicInitializationTestSupport { + struct Holder { + Atomic _explicitly_initialized; + Atomic _default_initialized; + Atomic _value_initialized; + + Holder() + : _explicitly_initialized(T()), + /* _default_initialized */ + _value_initialized{} + {} + }; + + struct HolderNoConstructor { + Atomic _default_initialized; + }; + + void test() { + T t = T(); + + { + Holder h; + + EXPECT_EQ(t, h._explicitly_initialized.load_relaxed()); + EXPECT_EQ(t, h._default_initialized.load_relaxed()); + EXPECT_EQ(t, h._value_initialized.load_relaxed()); + } + + { + Holder h{}; + + EXPECT_EQ(t, h._explicitly_initialized.load_relaxed()); + EXPECT_EQ(t, h._default_initialized.load_relaxed()); + EXPECT_EQ(t, h._value_initialized.load_relaxed()); + } + + { + alignas(Holder) char mem[sizeof(Holder)]; + memset(mem, 0xFF, sizeof(Holder)); + Holder* h = new (mem) Holder(); + + EXPECT_EQ(t, h->_explicitly_initialized.load_relaxed()); + EXPECT_EQ(t, h->_default_initialized.load_relaxed()); + EXPECT_EQ(t, h->_value_initialized.load_relaxed()); + } + + // No-constructor variant + + { + HolderNoConstructor h; + + EXPECT_EQ(t, h._default_initialized.load_relaxed()); + } + + { + HolderNoConstructor h{}; + + EXPECT_EQ(t, h._default_initialized.load_relaxed()); + } + + { + alignas(HolderNoConstructor) char mem[sizeof(HolderNoConstructor)]; + memset(mem, 0xFF, sizeof(HolderNoConstructor)); + HolderNoConstructor* h = new (mem) HolderNoConstructor(); + + EXPECT_EQ(t, h->_default_initialized.load_relaxed()); + } + } +}; + +TEST_VM(AtomicInitializationTest, byte) { + AtomicInitializationTestSupport().test(); +} + +TEST_VM(AtomicInitializationTest, integer) { + AtomicInitializationTestSupport().test(); +} + +TEST_VM(AtomicInitializationTest, pointer) { + AtomicInitializationTestSupport().test(); +} + template struct AtomicIntegerArithmeticTestSupport { Atomic _test_value; From 651e01b44747574a4882e7cdd9f6d3b54d2280f9 Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Wed, 4 Feb 2026 09:13:52 +0000 Subject: [PATCH 117/215] 8369393: NMT: poison the malloc header and footer under ASAN build Reviewed-by: jsjolen, phubner --- src/hotspot/share/nmt/mallocHeader.cpp | 2 +- src/hotspot/share/nmt/mallocHeader.hpp | 17 +++- src/hotspot/share/nmt/mallocHeader.inline.hpp | 39 +++++++--- src/hotspot/share/nmt/mallocTracker.cpp | 5 +- src/hotspot/share/nmt/mallocTracker.hpp | 9 --- src/hotspot/share/runtime/os.cpp | 65 +++++++--------- .../test_nmt_buffer_overflow_detection.cpp | 78 ++++++++++++++++++- .../gtest/nmt/test_nmt_cornercases.cpp | 7 +- .../NMTPrintMallocSiteOfCorruptedMemory.java | 2 + 9 files changed, 153 insertions(+), 71 deletions(-) diff --git a/src/hotspot/share/nmt/mallocHeader.cpp b/src/hotspot/share/nmt/mallocHeader.cpp index d88b5c790fb..e74e74fde48 100644 --- a/src/hotspot/share/nmt/mallocHeader.cpp +++ b/src/hotspot/share/nmt/mallocHeader.cpp @@ -75,4 +75,4 @@ void MallocHeader::print_block_on_error(outputStream* st, address bad_address, a // print one hex dump os::print_hex_dump(st, from1, to2, 1); } -} +} \ No newline at end of file diff --git a/src/hotspot/share/nmt/mallocHeader.hpp b/src/hotspot/share/nmt/mallocHeader.hpp index 45568243ef3..33e288dd8e4 100644 --- a/src/hotspot/share/nmt/mallocHeader.hpp +++ b/src/hotspot/share/nmt/mallocHeader.hpp @@ -27,10 +27,18 @@ #define SHARE_NMT_MALLOCHEADER_HPP #include "nmt/memTag.hpp" +#include "sanitizers/address.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/nativeCallStack.hpp" +// With ASAN, we omit NMT block integrity checks since ASAN does a better and faster +// job of alerting us to memory corruptions +#if INCLUDE_ASAN +#undef NMT_BLOCK_INTEGRITY_CHECKS +#else +#define NMT_BLOCK_INTEGRITY_CHECKS +#endif class outputStream; /* @@ -118,6 +126,7 @@ class MallocHeader { inline static OutTypeParam resolve_checked_impl(InTypeParam memblock); public: + static constexpr size_t footer_size = sizeof(uint16_t); // Contains all of the necessary data to to deaccount block with NMT. struct FreeInfo { const size_t size; @@ -126,8 +135,9 @@ public: }; inline MallocHeader(size_t size, MemTag mem_tag, uint32_t mst_marker); - - inline static size_t malloc_overhead() { return sizeof(MallocHeader) + sizeof(uint16_t); } + inline static size_t malloc_overhead() { return sizeof(MallocHeader) + footer_size; } + inline static MallocHeader* kill_block(void* memblock); + inline static void revive_block(void* memblock); inline size_t size() const { return _size; } inline MemTag mem_tag() const { return _mem_tag; } inline uint32_t mst_marker() const { return _mst_marker; } @@ -164,5 +174,4 @@ public: // This needs to be true on both 64-bit and 32-bit platforms STATIC_ASSERT(sizeof(MallocHeader) == (sizeof(uint64_t) * 2)); - -#endif // SHARE_NMT_MALLOCHEADER_HPP +#endif // SHARE_NMT_MALLOCHEADER_HPP \ No newline at end of file diff --git a/src/hotspot/share/nmt/mallocHeader.inline.hpp b/src/hotspot/share/nmt/mallocHeader.inline.hpp index 0ad0b3ddc81..09ab872c780 100644 --- a/src/hotspot/share/nmt/mallocHeader.inline.hpp +++ b/src/hotspot/share/nmt/mallocHeader.inline.hpp @@ -46,9 +46,6 @@ inline MallocHeader::MallocHeader(size_t size, MemTag mem_tag, uint32_t mst_mark } inline void MallocHeader::revive() { - assert(_canary == _header_canary_dead_mark, "must be dead"); - assert(get_footer() == _footer_canary_dead_mark, "must be dead"); - NOT_LP64(assert(_alt_canary == _header_alt_canary_dead_mark, "must be dead")); _canary = _header_canary_live_mark; NOT_LP64(_alt_canary = _header_alt_canary_live_mark); set_footer(_footer_canary_live_mark); @@ -96,16 +93,18 @@ inline bool MallocHeader::is_valid_malloced_pointer(const void* payload, char* m template inline OutTypeParam MallocHeader::resolve_checked_impl(InTypeParam memblock) { - char msg[256]; - address corruption = nullptr; - if (!is_valid_malloced_pointer(memblock, msg, sizeof(msg))) { - fatal("Not a valid malloc pointer: " PTR_FORMAT ": %s", p2i(memblock), msg); - } OutTypeParam header_pointer = (OutTypeParam)memblock - 1; - if (!header_pointer->check_block_integrity(msg, sizeof(msg), &corruption)) { - header_pointer->print_block_on_error(tty, corruption != nullptr ? corruption : (address)header_pointer, (address)header_pointer); - fatal("NMT has detected a memory corruption bug. Block at " PTR_FORMAT ": %s", p2i(memblock), msg); - } + #ifdef NMT_BLOCK_INTEGRITY_CHECKS + char msg[256]; + address corruption = nullptr; + if (!is_valid_malloced_pointer(memblock, msg, sizeof(msg))) { + fatal("Not a valid malloc pointer: " PTR_FORMAT ": %s", p2i(memblock), msg); + } + if (!header_pointer->check_block_integrity(msg, sizeof(msg), &corruption)) { + header_pointer->print_block_on_error(tty, corruption != nullptr ? corruption : (address)header_pointer, (address)header_pointer); + fatal("NMT has detected a memory corruption bug. Block at " PTR_FORMAT ": %s", p2i(memblock), msg); + } + #endif return header_pointer; } @@ -163,4 +162,20 @@ inline bool MallocHeader::check_block_integrity(char* msg, size_t msglen, addres return true; } +MallocHeader* MallocHeader::kill_block(void* memblock) { + MallocHeader* header = (MallocHeader*)memblock - 1; + ASAN_UNPOISON_MEMORY_REGION(header, sizeof(MallocHeader)); + ASAN_UNPOISON_MEMORY_REGION(header->footer_address(), footer_size); + resolve_checked(memblock); + header->mark_block_as_dead(); + return header; +} + +void MallocHeader::revive_block(void* memblock) { + MallocHeader* header = (MallocHeader*)memblock - 1; + header->revive(); + ASAN_POISON_MEMORY_REGION(header->footer_address(), footer_size); + ASAN_POISON_MEMORY_REGION(header, sizeof(MallocHeader)); +} + #endif // SHARE_NMT_MALLOCHEADER_INLINE_HPP diff --git a/src/hotspot/share/nmt/mallocTracker.cpp b/src/hotspot/share/nmt/mallocTracker.cpp index 2cf5034c0bf..532b6f41bd0 100644 --- a/src/hotspot/share/nmt/mallocTracker.cpp +++ b/src/hotspot/share/nmt/mallocTracker.cpp @@ -199,7 +199,7 @@ void* MallocTracker::record_malloc(void* malloc_base, size_t size, MemTag mem_ta assert(header2->mem_tag() == mem_tag, "Wrong memory tag"); } #endif - + MallocHeader::revive_block(memblock); return memblock; } @@ -207,7 +207,7 @@ void* MallocTracker::record_free_block(void* memblock) { assert(MemTracker::enabled(), "Sanity"); assert(memblock != nullptr, "precondition"); - MallocHeader* header = MallocHeader::resolve_checked(memblock); + MallocHeader* header = MallocHeader::kill_block(memblock); deaccount(header->free_info()); @@ -218,7 +218,6 @@ void* MallocTracker::record_free_block(void* memblock) { } header->mark_block_as_dead(); - return (void*)header; } diff --git a/src/hotspot/share/nmt/mallocTracker.hpp b/src/hotspot/share/nmt/mallocTracker.hpp index fc03faf7212..e96396ebbcb 100644 --- a/src/hotspot/share/nmt/mallocTracker.hpp +++ b/src/hotspot/share/nmt/mallocTracker.hpp @@ -311,15 +311,6 @@ class MallocTracker : AllStatic { // totally failproof. Only use this during debugging or when you can afford // signals popping up, e.g. when writing an hs_err file. static bool print_pointer_information(const void* p, outputStream* st); - - static inline MallocHeader* malloc_header(void *memblock) { - assert(memblock != nullptr, "null pointer"); - return (MallocHeader*)memblock -1; - } - static inline const MallocHeader* malloc_header(const void *memblock) { - assert(memblock != nullptr, "null pointer"); - return (const MallocHeader*)memblock -1; - } }; #endif // SHARE_NMT_MALLOCTRACKER_HPP diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 57971897400..3a5a5745095 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -702,57 +702,50 @@ void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallS if (new_outer_size < size) { return nullptr; } - - const size_t old_size = MallocTracker::malloc_header(memblock)->size(); - - // Observe MallocLimit - if ((size > old_size) && MemTracker::check_exceeds_limit(size - old_size, mem_tag)) { - return nullptr; - } + MallocHeader* header = MallocHeader::kill_block(memblock); + const size_t old_size = header->size(); // Perform integrity checks on and mark the old block as dead *before* calling the real realloc(3) since it // may invalidate the old block, including its header. - MallocHeader* header = MallocHeader::resolve_checked(memblock); assert(mem_tag == header->mem_tag(), "weird NMT type mismatch (new:\"%s\" != old:\"%s\")\n", NMTUtil::tag_to_name(mem_tag), NMTUtil::tag_to_name(header->mem_tag())); - const MallocHeader::FreeInfo free_info = header->free_info(); - - header->mark_block_as_dead(); - - // the real realloc - void* const new_outer_ptr = permit_forbidden_function::realloc(header, new_outer_size); - - if (new_outer_ptr == nullptr) { - // realloc(3) failed and the block still exists. - // We have however marked it as dead, revert this change. - header->revive(); - return nullptr; - } - // realloc(3) succeeded, variable header now points to invalid memory and we need to deaccount the old block. - MemTracker::deaccount(free_info); - - // After a successful realloc(3), we account the resized block with its new size - // to NMT. - void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, mem_tag, stack); + bool within_malloc_limit = !((size > old_size) && MemTracker::check_exceeds_limit(size - old_size, mem_tag)); + bool success = within_malloc_limit; + // Observe MallocLimit + if (success) { + // If realloc succeeds, the header is freed. Get FreeInfo before that. + MallocHeader::FreeInfo free_info = header->free_info(); + void* const new_outer_ptr = permit_forbidden_function::realloc(header, new_outer_size); + success = new_outer_ptr != nullptr; + if (success) { + // realloc(3) succeeded, variable header now points to invalid memory and we need to deaccount the old block. + MemTracker::deaccount(free_info); + // After a successful realloc(3), we account the resized block with its new size + // to NMT. + void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, mem_tag, stack); #ifdef ASSERT - assert(old_size == free_info.size, "Sanity"); - if (ZapCHeap && old_size < size) { - // We also zap the newly extended region. - ::memset((char*)new_inner_ptr + old_size, uninitBlockPad, size - old_size); - } + if (ZapCHeap && old_size < size) { + // We also zap the newly extended region. + ::memset((char*)new_inner_ptr + old_size, uninitBlockPad, size - old_size); + } #endif - rc = new_inner_ptr; - + rc = new_inner_ptr; + } + } + if (!success) { + // realloc(3) failed and the block still exists. + // We have however marked it as dead, revert this change. + MallocHeader::revive_block(memblock); + return nullptr; + } } else { - // NMT disabled. rc = permit_forbidden_function::realloc(memblock, size); if (rc == nullptr) { return nullptr; } - } DEBUG_ONLY(break_if_ptr_caught(rc);) diff --git a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp index 5752d6df75d..c65808d3f4d 100644 --- a/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_buffer_overflow_detection.cpp @@ -32,8 +32,6 @@ #include "unittest.hpp" #include "testutils.hpp" -#if !INCLUDE_ASAN - // This prefix shows up on any c heap corruption NMT detects. If unsure which assert will // come, just use this one. #define COMMON_NMT_HEAP_CORRUPTION_MESSAGE_PREFIX "NMT has detected a memory corruption bug." @@ -52,6 +50,8 @@ /////// +#if !INCLUDE_ASAN + static void test_overwrite_front() { address p = (address) os::malloc(1, mtTest); *(p - 1) = 'a'; @@ -190,4 +190,78 @@ TEST_VM_FATAL_ERROR_MSG(NMT, memory_corruption_call_stack, ".*header canary.*") os::free(p); } +#else // ASAN is enabled + +#define DEFINE_ASAN_TEST(test_function) \ + DEFINE_TEST(test_function, ".*AddressSanitizer.*") + +static void test_write_header() { + const size_t SIZE = 10; + char* p = (char*)os::malloc(SIZE, mtTest); + // sizeof(MallocHeader) == 16, pick anywheree in [p - 16, p) + *(uint16_t*)((char*)p - 5) = 1; +} + +static void test_read_header() { + const size_t SIZE = 10; + char* p = (char*)os::malloc(SIZE, mtTest); + // sizeof(MallocHeader) == 16, pick anywheree in [p - 16, p) + uint16_t read_canary = *(uint16_t*)((char*)p - 5); +} + +static void test_write_footer() { + const size_t SIZE = 10; + char* p = (char*)os::malloc(SIZE, mtTest); + uint16_t* footer_ptr = (uint16_t*)(p + SIZE); + *footer_ptr = 1; +} + +static void test_read_footer() { + const size_t SIZE = 10; + char* p = (char*)os::malloc(SIZE, mtTest); + uint16_t* footer_ptr = (uint16_t*)(p + SIZE); + uint16_t read_footer = *footer_ptr; +} + +static void test_write_header_after_realloc() { + const size_t SIZE = 10; + char* p = (char*)os::malloc(SIZE, mtTest); + p = (char*)os::realloc(p, 2 * SIZE, mtTest); + // sizeof(MallocHeader) == 16, pick anywheree in [p - 16, p) + *(uint16_t*)((char*)p - 5) = 1; +} + +static void test_read_header_after_realloc() { + const size_t SIZE = 10; + char* p = (char*)os::malloc(SIZE, mtTest); + p = (char*)os::realloc(p, 2 * SIZE, mtTest); + // sizeof(MallocHeader) == 16, pick anywheree in [p - 16, p) + uint16_t read_canary = *(uint16_t*)((char*)p - 5); +} + +static void test_write_footer_after_realloc() { + const size_t SIZE = 10; + char* p = (char*)os::malloc(SIZE, mtTest); + p = (char*)os::realloc(p, 2 * SIZE, mtTest); + uint16_t* footer_ptr = (uint16_t*)(p + 2 * SIZE); + *footer_ptr = 1; +} + +static void test_read_footer_after_realloc() { + const size_t SIZE = 10; + char* p = (char*)os::malloc(SIZE, mtTest); + p = (char*)os::realloc(p, 2 * SIZE, mtTest); + uint16_t* footer_ptr = (uint16_t*)(p + 2 * SIZE); + uint16_t read_footer = *footer_ptr; +} + +DEFINE_ASAN_TEST(test_write_header); +DEFINE_ASAN_TEST(test_read_header); +DEFINE_ASAN_TEST(test_write_footer); +DEFINE_ASAN_TEST(test_read_footer); +DEFINE_ASAN_TEST(test_write_header_after_realloc); +DEFINE_ASAN_TEST(test_read_header_after_realloc); +DEFINE_ASAN_TEST(test_write_footer_after_realloc); +DEFINE_ASAN_TEST(test_read_footer_after_realloc); + #endif // !INCLUDE_ASAN diff --git a/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp b/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp index d1da65cb396..24ab9888521 100644 --- a/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp @@ -88,7 +88,6 @@ TEST_VM(NMT, realloc_failure_overflowing_size) { TEST_VM(NMT, realloc_failure_gigantic_size) { check_failing_realloc(SIZE_MAX - M); } -#endif // !INCLUDE_ASAN static void* do_realloc(void* p, size_t old_size, size_t new_size, uint8_t old_content, bool check_nmt_header) { @@ -154,8 +153,8 @@ TEST_VM(NMT, HeaderKeepsIntegrityAfterRevival) { size_t some_size = 16; void* p = os::malloc(some_size, mtTest); ASSERT_NOT_NULL(p) << "Failed to malloc()"; - MallocHeader* hdr = MallocTracker::malloc_header(p); - hdr->mark_block_as_dead(); - hdr->revive(); + MallocHeader* hdr = MallocHeader::kill_block(p); + MallocHeader::revive_block(p); check_expected_malloc_header(p, mtTest, some_size); } +#endif // !INCLUDE_ASAN \ No newline at end of file diff --git a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java index 7f0f1be929b..f1d4964d6cf 100644 --- a/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java +++ b/test/hotspot/jtreg/runtime/NMT/NMTPrintMallocSiteOfCorruptedMemory.java @@ -24,6 +24,8 @@ /* * @test * @summary Check the allocation-site stack trace of a corrupted memory at free() time + * @comment Under ASAN build, memory corruption is reported by ASAN runtime and not JVM. + * @requires !vm.asan * @modules java.base/jdk.internal.misc * @library /test/lib * @build jdk.test.whitebox.WhiteBox From c5e973e03418d6528fce1aa4a68e0b07a82036ac Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Wed, 4 Feb 2026 09:14:24 +0000 Subject: [PATCH 118/215] 8374622: StressIncrementalInlining should also randomize the processing order Reviewed-by: thartmann, chagedorn, dfenacci --- src/hotspot/share/opto/compile.cpp | 31 +++++++++++++++++++++++------- src/hotspot/share/opto/compile.hpp | 8 ++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index c4c9445b61a..77b7636a150 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2171,6 +2171,21 @@ void Compile::inline_incrementally_cleanup(PhaseIterGVN& igvn) { print_method(PHASE_INCREMENTAL_INLINE_CLEANUP, 3); } +template +static void shuffle_array(Compile& C, GrowableArray& array) { + if (array.length() < 2) { + return; + } + for (uint i = array.length() - 1; i >= 1; i--) { + uint j = C.random() % (i + 1); + swap(array.at(i), array.at(j)); + } +} + +void Compile::shuffle_late_inlines() { + shuffle_array(*C, _late_inlines); +} + // Perform incremental inlining until bound on number of live nodes is reached void Compile::inline_incrementally(PhaseIterGVN& igvn) { TracePhase tp(_t_incrInline); @@ -2178,6 +2193,10 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { set_inlining_incrementally(true); uint low_live_nodes = 0; + if (StressIncrementalInlining) { + shuffle_late_inlines(); + } + while (_late_inlines.length() > 0) { if (live_nodes() > (uint)LiveNodeCountInliningCutoff) { if (low_live_nodes < (uint)LiveNodeCountInliningCutoff * 8 / 10) { @@ -2250,6 +2269,10 @@ void Compile::process_late_inline_calls_no_inline(PhaseIterGVN& igvn) { assert(_modified_nodes == nullptr, "not allowed"); assert(_late_inlines.length() > 0, "sanity"); + if (StressIncrementalInlining) { + shuffle_late_inlines(); + } + while (_late_inlines.length() > 0) { igvn_worklist()->ensure_empty(); // should be done with igvn @@ -5141,13 +5164,7 @@ void CloneMap::dump(node_idx_t key, outputStream* st) const { } void Compile::shuffle_macro_nodes() { - if (_macro_nodes.length() < 2) { - return; - } - for (uint i = _macro_nodes.length() - 1; i >= 1; i--) { - uint j = C->random() % (i + 1); - swap(_macro_nodes.at(i), _macro_nodes.at(j)); - } + shuffle_array(*C, _macro_nodes); } // Move Allocate nodes to the start of the list diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index d9a8ea15724..eb6be669f24 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -795,6 +795,7 @@ public: void remove_from_merge_stores_igvn(Node* n); void process_for_merge_stores_igvn(PhaseIterGVN& igvn); + void shuffle_late_inlines(); void shuffle_macro_nodes(); void sort_macro_nodes(); @@ -1059,6 +1060,13 @@ public: // Record this CallGenerator for inlining at the end of parsing. void add_late_inline(CallGenerator* cg) { _late_inlines.insert_before(_late_inlines_pos, cg); + if (StressIncrementalInlining) { + assert(_late_inlines_pos < _late_inlines.length(), "unthinkable!"); + if (_late_inlines.length() - _late_inlines_pos >= 2) { + int j = (C->random() % (_late_inlines.length() - _late_inlines_pos)) + _late_inlines_pos; + swap(_late_inlines.at(_late_inlines_pos), _late_inlines.at(j)); + } + } _late_inlines_pos++; } From 848171a6ccc6c3610b8de0c871d0082204369bee Mon Sep 17 00:00:00 2001 From: Ivan Walulya Date: Wed, 4 Feb 2026 09:51:31 +0000 Subject: [PATCH 119/215] 8374782: Parallel: Remove specialized objArray iteration code Reviewed-by: tschatzl, ayang --- .../share/gc/parallel/psPromotionManager.cpp | 34 ++++++------------- .../share/gc/parallel/psPromotionManager.hpp | 3 +- .../gc/parallel/psPromotionManager.inline.hpp | 2 +- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.cpp b/src/hotspot/share/gc/parallel/psPromotionManager.cpp index a41a9403082..d6208755374 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.cpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.cpp @@ -43,6 +43,7 @@ #include "memory/resourceArea.hpp" #include "oops/access.inline.hpp" #include "oops/compressedOops.inline.hpp" +#include "oops/oopsHierarchy.hpp" #include "utilities/checkedCast.hpp" PaddedEnd* PSPromotionManager::_manager_array = nullptr; @@ -248,30 +249,19 @@ void PSPromotionManager::flush_labs() { } } -template -void PSPromotionManager::process_array_chunk_work(oop obj, int start, int end) { - assert(start <= end, "invariant"); - T* const base = (T*)objArrayOop(obj)->base(); - T* p = base + start; - T* const chunk_end = base + end; - while (p < chunk_end) { - claim_or_forward_depth(p); - ++p; - } +void PSPromotionManager::process_array_chunk(objArrayOop obj, size_t start, size_t end) { + PSPushContentsClosure pcc(this); + obj->oop_iterate_elements_range(&pcc, + checked_cast(start), + checked_cast(end)); } void PSPromotionManager::process_array_chunk(PartialArrayState* state, bool stolen) { // Access before release by claim(). - oop new_obj = state->destination(); + objArrayOop to_array = objArrayOop(state->destination()); PartialArraySplitter::Claim claim = _partial_array_splitter.claim(state, &_claimed_stack_depth, stolen); - int start = checked_cast(claim._start); - int end = checked_cast(claim._end); - if (UseCompressedOops) { - process_array_chunk_work(new_obj, start, end); - } else { - process_array_chunk_work(new_obj, start, end); - } + process_array_chunk(to_array, claim._start, claim._end); } void PSPromotionManager::push_objArray(oop old_obj, oop new_obj) { @@ -284,12 +274,8 @@ void PSPromotionManager::push_objArray(oop old_obj, oop new_obj) { size_t initial_chunk_size = // The source array is unused when processing states. _partial_array_splitter.start(&_claimed_stack_depth, nullptr, to_array, array_length); - int end = checked_cast(initial_chunk_size); - if (UseCompressedOops) { - process_array_chunk_work(to_array, 0, end); - } else { - process_array_chunk_work(to_array, 0, end); - } + + process_array_chunk(to_array, 0, initial_chunk_size); } oop PSPromotionManager::oop_promotion_failed(oop obj, markWord obj_mark) { diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.hpp index 44df708eea4..2b0fc56c0bf 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.hpp @@ -97,9 +97,8 @@ class PSPromotionManager { inline static PSPromotionManager* manager_array(uint index); - template void process_array_chunk_work(oop obj, - int start, int end); void process_array_chunk(PartialArrayState* state, bool stolen); + void process_array_chunk(objArrayOop obj, size_t start, size_t end); void push_objArray(oop old_obj, oop new_obj); inline void promotion_trace_event(oop new_obj, Klass* klass, size_t obj_size, diff --git a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp index f1fd49c7dfe..9e904e44b22 100644 --- a/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psPromotionManager.inline.hpp @@ -51,7 +51,7 @@ inline PSPromotionManager* PSPromotionManager::manager_array(uint index) { } template -inline void PSPromotionManager::claim_or_forward_depth(T* p) { +ALWAYSINLINE void PSPromotionManager::claim_or_forward_depth(T* p) { assert(ParallelScavengeHeap::heap()->is_in(p), "pointer outside heap"); T heap_oop = RawAccess<>::oop_load(p); if (PSScavenge::is_obj_in_young(heap_oop)) { From 13029e128ac7183af83234a031c62462aae14fad Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Wed, 4 Feb 2026 10:11:25 +0000 Subject: [PATCH 120/215] 8372942: AArch64: Set JVM flags for Neoverse V3AE core Reviewed-by: aph, fgao --- .../cpu/aarch64/vm_version_aarch64.cpp | 28 ++++++------------- .../cpu/aarch64/vm_version_aarch64.hpp | 27 ++++++++++++++++-- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 659c231464a..3fa85f8f47d 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -201,16 +201,14 @@ void VM_Version::initialize() { } } - // Cortex A53 - if (_cpu == CPU_ARM && model_is(0xd03)) { + if (_cpu == CPU_ARM && model_is(CPU_MODEL_ARM_CORTEX_A53)) { set_feature(CPU_A53MAC); if (FLAG_IS_DEFAULT(UseSIMDForArrayEquals)) { FLAG_SET_DEFAULT(UseSIMDForArrayEquals, false); } } - // Cortex A73 - if (_cpu == CPU_ARM && model_is(0xd09)) { + if (_cpu == CPU_ARM && model_is(CPU_MODEL_ARM_CORTEX_A73)) { if (FLAG_IS_DEFAULT(SoftwarePrefetchHintDistance)) { FLAG_SET_DEFAULT(SoftwarePrefetchHintDistance, -1); } @@ -220,16 +218,11 @@ void VM_Version::initialize() { } } - // Neoverse - // N1: 0xd0c - // N2: 0xd49 - // N3: 0xd8e - // V1: 0xd40 - // V2: 0xd4f - // V3: 0xd84 - if (_cpu == CPU_ARM && (model_is(0xd0c) || model_is(0xd49) || - model_is(0xd40) || model_is(0xd4f) || - model_is(0xd8e) || model_is(0xd84))) { + if (_cpu == CPU_ARM && + model_is_in({ CPU_MODEL_ARM_NEOVERSE_N1, CPU_MODEL_ARM_NEOVERSE_V1, + CPU_MODEL_ARM_NEOVERSE_N2, CPU_MODEL_ARM_NEOVERSE_V2, + CPU_MODEL_ARM_NEOVERSE_N3, CPU_MODEL_ARM_NEOVERSE_V3, + CPU_MODEL_ARM_NEOVERSE_V3AE })) { if (FLAG_IS_DEFAULT(UseSIMDForMemoryOps)) { FLAG_SET_DEFAULT(UseSIMDForMemoryOps, true); } @@ -261,12 +254,9 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseCRC32, false); } - // Neoverse - // V1: 0xd40 - // V2: 0xd4f - // V3: 0xd84 if (_cpu == CPU_ARM && - (model_is(0xd40) || model_is(0xd4f) || model_is(0xd84))) { + model_is_in({ CPU_MODEL_ARM_NEOVERSE_V1, CPU_MODEL_ARM_NEOVERSE_V2, + CPU_MODEL_ARM_NEOVERSE_V3, CPU_MODEL_ARM_NEOVERSE_V3AE })) { if (FLAG_IS_DEFAULT(UseCryptoPmullForCRC32)) { FLAG_SET_DEFAULT(UseCryptoPmullForCRC32, true); } diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp index 17087d243d3..38b112d9936 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp @@ -30,6 +30,8 @@ #include "runtime/abstract_vm_version.hpp" #include "utilities/sizes.hpp" +#include + class stringStream; #define BIT_MASK(flag) (1ULL<<(flag)) @@ -112,14 +114,26 @@ public: CPU_APPLE = 'a', }; -enum Ampere_CPU_Model { + enum Ampere_CPU_Model { CPU_MODEL_EMAG = 0x0, /* CPU implementer is CPU_AMCC */ CPU_MODEL_ALTRA = 0xd0c, /* CPU implementer is CPU_ARM, Neoverse N1 */ CPU_MODEL_ALTRAMAX = 0xd0c, /* CPU implementer is CPU_ARM, Neoverse N1 */ CPU_MODEL_AMPERE_1 = 0xac3, /* CPU implementer is CPU_AMPERE */ CPU_MODEL_AMPERE_1A = 0xac4, /* CPU implementer is CPU_AMPERE */ CPU_MODEL_AMPERE_1B = 0xac5 /* AMPERE_1B core Implements ARMv8.7 with CSSC, MTE, SM3/SM4 extensions */ -}; + }; + + enum ARM_CPU_Model { + CPU_MODEL_ARM_CORTEX_A53 = 0xd03, + CPU_MODEL_ARM_CORTEX_A73 = 0xd09, + CPU_MODEL_ARM_NEOVERSE_N1 = 0xd0c, + CPU_MODEL_ARM_NEOVERSE_V1 = 0xd40, + CPU_MODEL_ARM_NEOVERSE_N2 = 0xd49, + CPU_MODEL_ARM_NEOVERSE_V2 = 0xd4f, + CPU_MODEL_ARM_NEOVERSE_V3AE = 0xd83, + CPU_MODEL_ARM_NEOVERSE_V3 = 0xd84, + CPU_MODEL_ARM_NEOVERSE_N3 = 0xd8e, + }; #define CPU_FEATURE_FLAGS(decl) \ decl(FP, fp, 0) \ @@ -181,6 +195,15 @@ enum Ampere_CPU_Model { return _model == cpu_model || _model2 == cpu_model; } + static bool model_is_in(std::initializer_list cpu_models) { + for (const int& cpu_model : cpu_models) { + if (_model == cpu_model || _model2 == cpu_model) { + return true; + } + } + return false; + } + static bool is_zva_enabled() { return 0 <= _zva_length; } static int zva_length() { assert(is_zva_enabled(), "ZVA not available"); From d7523ec8d2255675547c0746d076efd7af5dd5af Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Wed, 4 Feb 2026 10:13:41 +0000 Subject: [PATCH 121/215] 8376031: HttpsURLConnection.getServerCertificates() throws "java.lang.IllegalStateException: connection not yet open" for the HEAD method Reviewed-by: jpai --- .../www/protocol/http/HttpURLConnection.java | 26 +- .../AbstractDelegateHttpsURLConnection.java | 113 +++++-- .../net/www/protocol/https/HttpsClient.java | 71 +--- .../GetServerCertificates.java | 302 ++++++++++++++++++ 4 files changed, 416 insertions(+), 96 deletions(-) create mode 100644 test/jdk/sun/net/www/protocol/https/HttpsURLConnection/GetServerCertificates.java diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 89ad0cc48ed..3a915cf96df 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 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 @@ -1677,11 +1677,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { if (method.equals("HEAD") || cl == 0 || respCode == HTTP_NOT_MODIFIED || respCode == HTTP_NO_CONTENT) { - - http.finished(); - http = null; - inputStream = new EmptyInputStream(); - connected = false; + noResponseBody(); } if (respCode == 200 || respCode == 203 || respCode == 206 || @@ -1763,6 +1759,24 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } } + /** + * This method is called when a response with no response + * body is received, and arrange for the http client to + * be returned to the pool (or released) immediately when + * possible. + * @apiNote Used by {@link sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection} + * to preserve the TLS information after receiving an empty body. + * @implSpec + * Subclasses that override this method should call the super class + * implementation. + */ + protected void noResponseBody() { + http.finished(); + http = null; + inputStream = new EmptyInputStream(); + connected = false; + } + /* * Creates a chained exception that has the same type as * original exception and with the same message. Right now, diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java index 7bf8280a7ad..1415658e34d 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.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 @@ -51,6 +51,7 @@ import sun.net.www.protocol.http.HttpCallerInfo; public abstract class AbstractDelegateHttpsURLConnection extends HttpURLConnection { + private SSLSession savedSession = null; protected AbstractDelegateHttpsURLConnection(URL url, sun.net.www.protocol.http.Handler handler) throws IOException { this(url, null, handler); @@ -92,6 +93,7 @@ public abstract class AbstractDelegateHttpsURLConnection extends public void setNewClient (URL url, boolean useCache) throws IOException { int readTimeout = getReadTimeout(); + savedSession = null; http = HttpsClient.New (getSSLSocketFactory(), url, getHostnameVerifier(), @@ -184,6 +186,7 @@ public abstract class AbstractDelegateHttpsURLConnection extends if (!http.isCachedConnection() && http.needsTunneling()) { doTunneling(); } + savedSession = null; ((HttpsClient)http).afterConnect(); } @@ -204,6 +207,19 @@ public abstract class AbstractDelegateHttpsURLConnection extends useCache, connectTimeout, this); } + @Override + protected void noResponseBody() { + savedSession = ((HttpsClient)http).getSSLSession(); + super.noResponseBody(); + } + + private SSLSession session() { + if (http instanceof HttpsClient https) { + return https.getSSLSession(); + } + return savedSession; + } + /** * Returns the cipher suite in use on this connection. */ @@ -211,11 +227,12 @@ public abstract class AbstractDelegateHttpsURLConnection extends if (cachedResponse != null) { return ((SecureCacheResponse)cachedResponse).getCipherSuite(); } - if (http == null) { + + var session = session(); + if (session == null) { throw new IllegalStateException("connection not yet open"); - } else { - return ((HttpsClient)http).getCipherSuite (); } + return session.getCipherSuite(); } /** @@ -231,11 +248,12 @@ public abstract class AbstractDelegateHttpsURLConnection extends return l.toArray(new java.security.cert.Certificate[0]); } } - if (http == null) { + + var session = session(); + if (session == null) { throw new IllegalStateException("connection not yet open"); - } else { - return (((HttpsClient)http).getLocalCertificates ()); } + return session.getLocalCertificates(); } /** @@ -256,11 +274,11 @@ public abstract class AbstractDelegateHttpsURLConnection extends } } - if (http == null) { + var session = session(); + if (session == null) { throw new IllegalStateException("connection not yet open"); - } else { - return (((HttpsClient)http).getServerCertificates ()); } + return session.getPeerCertificates(); } /** @@ -274,11 +292,11 @@ public abstract class AbstractDelegateHttpsURLConnection extends return ((SecureCacheResponse)cachedResponse).getPeerPrincipal(); } - if (http == null) { + var session = session(); + if (session == null) { throw new IllegalStateException("connection not yet open"); - } else { - return (((HttpsClient)http).getPeerPrincipal()); } + return getPeerPrincipal(session); } /** @@ -291,11 +309,11 @@ public abstract class AbstractDelegateHttpsURLConnection extends return ((SecureCacheResponse)cachedResponse).getLocalPrincipal(); } - if (http == null) { + var session = session(); + if (session == null) { throw new IllegalStateException("connection not yet open"); - } else { - return (((HttpsClient)http).getLocalPrincipal()); } + return getLocalPrincipal(session); } SSLSession getSSLSession() { @@ -307,11 +325,12 @@ public abstract class AbstractDelegateHttpsURLConnection extends } } - if (http == null) { + var session = session(); + if (session == null) { throw new IllegalStateException("connection not yet open"); } - return ((HttpsClient)http).getSSLSession(); + return session; } /* @@ -354,7 +373,7 @@ public abstract class AbstractDelegateHttpsURLConnection extends } HttpsClient https = (HttpsClient)http; try { - Certificate[] certs = https.getServerCertificates(); + Certificate[] certs = https.getSSLSession().getPeerCertificates(); if (certs[0] instanceof X509Certificate x509Cert) { return new HttpCallerInfo(url, proxy, port, x509Cert, authenticator); } @@ -372,7 +391,7 @@ public abstract class AbstractDelegateHttpsURLConnection extends } HttpsClient https = (HttpsClient)http; try { - Certificate[] certs = https.getServerCertificates(); + Certificate[] certs = https.getSSLSession().getPeerCertificates(); if (certs[0] instanceof X509Certificate x509Cert) { return new HttpCallerInfo(url, x509Cert, authenticator); } @@ -381,4 +400,58 @@ public abstract class AbstractDelegateHttpsURLConnection extends } return super.getHttpCallerInfo(url, authenticator); } + + @Override + public void disconnect() { + super.disconnect(); + savedSession = null; + } + + /** + * Returns the principal with which the server authenticated + * itself, or throw a SSLPeerUnverifiedException if the + * server did not authenticate. + * @param session The {@linkplain #getSSLSession() SSL session} + */ + private static Principal getPeerPrincipal(SSLSession session) + throws SSLPeerUnverifiedException + { + Principal principal; + try { + principal = session.getPeerPrincipal(); + } catch (AbstractMethodError e) { + // if the provider does not support it, fallback to peer certs. + // return the X500Principal of the end-entity cert. + java.security.cert.Certificate[] certs = + session.getPeerCertificates(); + principal = ((X509Certificate)certs[0]).getSubjectX500Principal(); + } + return principal; + } + + /** + * Returns the principal the client sent to the + * server, or null if the client did not authenticate. + * @param session The {@linkplain #getSSLSession() SSL session} + */ + private static Principal getLocalPrincipal(SSLSession session) + { + Principal principal; + try { + principal = session.getLocalPrincipal(); + } catch (AbstractMethodError e) { + principal = null; + // if the provider does not support it, fallback to local certs. + // return the X500Principal of the end-entity cert. + java.security.cert.Certificate[] certs = + session.getLocalCertificates(); + if (certs != null) { + principal = ((X509Certificate)certs[0]).getSubjectX500Principal(); + } + } + return principal; + } + + + } diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java index 9f1d7b07021..f5804cd83bd 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java +++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.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 @@ -599,75 +599,6 @@ final class HttpsClient extends HttpClient } } - /** - * Returns the cipher suite in use on this connection. - */ - String getCipherSuite() { - return session.getCipherSuite(); - } - - /** - * Returns the certificate chain the client sent to the - * server, or null if the client did not authenticate. - */ - public java.security.cert.Certificate [] getLocalCertificates() { - return session.getLocalCertificates(); - } - - /** - * Returns the certificate chain with which the server - * authenticated itself, or throw a SSLPeerUnverifiedException - * if the server did not authenticate. - */ - java.security.cert.Certificate [] getServerCertificates() - throws SSLPeerUnverifiedException - { - return session.getPeerCertificates(); - } - - /** - * Returns the principal with which the server authenticated - * itself, or throw a SSLPeerUnverifiedException if the - * server did not authenticate. - */ - Principal getPeerPrincipal() - throws SSLPeerUnverifiedException - { - Principal principal; - try { - principal = session.getPeerPrincipal(); - } catch (AbstractMethodError e) { - // if the provider does not support it, fallback to peer certs. - // return the X500Principal of the end-entity cert. - java.security.cert.Certificate[] certs = - session.getPeerCertificates(); - principal = ((X509Certificate)certs[0]).getSubjectX500Principal(); - } - return principal; - } - - /** - * Returns the principal the client sent to the - * server, or null if the client did not authenticate. - */ - Principal getLocalPrincipal() - { - Principal principal; - try { - principal = session.getLocalPrincipal(); - } catch (AbstractMethodError e) { - principal = null; - // if the provider does not support it, fallback to local certs. - // return the X500Principal of the end-entity cert. - java.security.cert.Certificate[] certs = - session.getLocalCertificates(); - if (certs != null) { - principal = ((X509Certificate)certs[0]).getSubjectX500Principal(); - } - } - return principal; - } - /** * Returns the {@code SSLSession} in use on this connection. */ diff --git a/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/GetServerCertificates.java b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/GetServerCertificates.java new file mode 100644 index 00000000000..5de0d7aeb1a --- /dev/null +++ b/test/jdk/sun/net/www/protocol/https/HttpsURLConnection/GetServerCertificates.java @@ -0,0 +1,302 @@ +/* + * 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 8376031 + * @modules jdk.httpserver + * @library /test/lib + * @summary Ensure HttpsURLConnection::getServerCertificates does not + * throw after calling getResponseCode() if the response doesn't have + * a body. + * @run main/othervm ${test.main.class} + * @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class} + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import jdk.test.lib.net.SimpleSSLContext; +import jdk.test.lib.net.URIBuilder; + +import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY; + + +public class GetServerCertificates { + + static final String URI_PATH = "/GetServerCertificates/"; + static final String BODY = "Go raibh maith agat"; + enum TESTS { + HEAD("head", 200, "HEAD"), + NOBODY("nobody", 200, "GET", "POST"), + S204("204", 204, "GET", "POST"), + S304("304", 304, "GET", "POST"), + S200("200", 200, "GET", "POST"); + final String test; + final int code; + final List methods; + private TESTS(String test, int code, String... methods) { + this.test = test; + this.code = code; + this.methods = List.of(methods); + } + boolean isFor(String path) { + return path != null && path.endsWith("/" + test); + } + + String test() { return test; } + int code() { return code; } + List methods() { return methods; } + static Optional fromPath(String path) { + return Stream.of(values()) + .filter(test -> test.isFor(path)) + .findFirst(); + } + } + + void test(String[] args) throws Exception { + SSLContext.setDefault(SimpleSSLContext.findSSLContext()); + HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + HttpServer server = startHttpServer(); + try { + InetSocketAddress address = server.getAddress(); + URI uri = URIBuilder.newBuilder() + .scheme("https") + .host(address.getAddress()) + .port(address.getPort()) + .path(URI_PATH) + .build(); + for (var test : TESTS.values()) { + for (String method : test.methods()) { + doClient(method, uri, test); + } + } + + } finally { + server.stop(1000); + } + } + + void doClient(String method, URI baseUri, TESTS test) throws Exception { + assert baseUri.getRawQuery() == null; + assert baseUri.getRawFragment() == null; + assert test.methods().contains(method); + + String uriStr = baseUri.toString(); + if (!uriStr.endsWith("/")) uriStr = uriStr + "/"; + + URI uri = new URI(uriStr + test.test()); + assert uri.toString().endsWith("/" + test.test()); + int code = test.code(); + System.out.println("doClient(%s, %s, %s)" + .formatted(method, test.test(), test.code)); + + // first request - should create a TCP connection + HttpsURLConnection uc = (HttpsURLConnection) + uri.toURL().openConnection(Proxy.NO_PROXY); + if (!"GET".equals(method)) { + uc.setRequestMethod(method); + } + try { + uc.getServerCertificates(); + throw new AssertionError("Expected IllegalStateException not thrown"); + } catch (IllegalStateException ise) { + System.out.println("Got expected ISE: " + ise); + } + int resp = uc.getResponseCode(); + check(resp == code, "Unexpected response code. Expected %s, got %s" + .formatted(code, resp)); + + check(uc.getServerCertificates()); + if (test == TESTS.S200) { + byte[] bytes = uc.getInputStream().readAllBytes(); + String body = new String(bytes, StandardCharsets.UTF_8); + System.out.println("body: " + body); + check(BODY.equals(body), "Unexpected response body. Expected \"%s\", got \"%s\"" + .formatted(BODY, body)); + } + + // second request - should go on the same TCP connection. + // We don't have a reliable way to test that, and it could + // go on a new TCP connection if the previous connection + // was already closed. It is not an issue either way. + uc = (HttpsURLConnection) + uri.toURL().openConnection(Proxy.NO_PROXY); + if (!"GET".equals(method)) { + uc.setRequestMethod(method); + } + try { + uc.getServerCertificates(); + throw new AssertionError("Expected IllegalStateException not thrown"); + } catch (IllegalStateException ise) { + System.out.println("Got expected ISE: " + ise); + } + resp = uc.getResponseCode(); + check(resp == code, "Unexpected response code. Expected %s, got %s" + .formatted(code, resp)); + + check(uc.getServerCertificates()); + if (test == TESTS.S200) { + byte[] bytes = uc.getInputStream().readAllBytes(); + String body = new String(bytes, StandardCharsets.UTF_8); + System.out.println("body: " + body); + check(BODY.equals(body), "Unexpected response body. Expected \"%s\", got \"%s\"" + .formatted(BODY, body)); + } + + uc.disconnect(); + try { + uc.getServerCertificates(); + throw new AssertionError("Expected IllegalStateException not thrown"); + } catch (IllegalStateException ise) { + System.out.println("Got expected ISE: " + ise); + } + + // third request - forces the connection to close + // after use so that we don't find any connection in the pool + // for the next test case, assuming there was only + // one connection in the first place. + // Again there's no easy way to verify that the pool + // is empty (and it's not really necessary to bother) + uc = (HttpsURLConnection) + uri.toURL().openConnection(Proxy.NO_PROXY); + if (!"GET".equals(method)) { + uc.setRequestMethod(method); + } + uc.setRequestProperty("Connection", "close"); + try { + uc.getServerCertificates(); + throw new AssertionError("Expected IllegalStateException not thrown"); + } catch (IllegalStateException ise) { + System.out.println("Got expected ISE: " + ise); + } + resp = uc.getResponseCode(); + check(resp == code, "Unexpected response code. Expected %s, got %s" + .formatted(code, resp)); + check(uc.getServerCertificates()); + uc.disconnect(); + try { + uc.getServerCertificates(); + throw new AssertionError("Expected IllegalStateException not thrown"); + } catch (IllegalStateException ise) { + System.out.println("Got expected ISE: " + ise); + } + } + + // HTTP Server + HttpServer startHttpServer() throws IOException { + InetAddress localhost = InetAddress.getLoopbackAddress(); + HttpsServer httpServer = HttpsServer + .create(new InetSocketAddress(localhost, 0), 0); + var configurator = new HttpsConfigurator(SimpleSSLContext.findSSLContext()); + httpServer.setHttpsConfigurator(configurator); + httpServer.createContext(URI_PATH, new SimpleHandler()); + httpServer.start(); + return httpServer; + } + + static class SimpleHandler implements HttpHandler { + @Override + public void handle(HttpExchange t) throws IOException { + try { + String path = t.getRequestURI().getRawPath(); + var test = TESTS.fromPath(path); + if (!path.startsWith(URI_PATH) || !test.isPresent()) { + t.getRequestBody().close(); + t.getResponseHeaders().add("Connection", "close"); + t.sendResponseHeaders(421, RSPBODY_EMPTY); + t.close(); + return; + } + try (var is = t.getRequestBody()) { + is.readAllBytes(); + } + switch (test.get()) { + case S204, S304, NOBODY -> + t.sendResponseHeaders(test.get().code(), RSPBODY_EMPTY); + case S200 -> { + byte[] bytes = BODY.getBytes(StandardCharsets.UTF_8); + t.sendResponseHeaders(test.get().code(), bytes.length); + try (var os = t.getResponseBody()) { + os.write(bytes); + } + } + case HEAD -> { + assert t.getRequestMethod().equals("HEAD"); + byte[] bytes = BODY.getBytes(StandardCharsets.UTF_8); + t.sendResponseHeaders(test.get().code(), bytes.length); + } + } + t.close(); + } catch (Throwable error) { + error.printStackTrace(); + throw error; + } + } + } + + volatile int passed = 0, failed = 0; + boolean debug = false; + void pass() {passed++;} + void fail() {failed++;} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void debug(String message) { if (debug) System.out.println(message); } + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void check(java.security.cert.Certificate[] certs) { + // Use List.of to check that certs is not null and does not + // contain null. NullPointerException will be thrown here + // if that happens, which will make the test fail. + check(!List.of(certs).isEmpty(), "no certificates returned"); + } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} From 84e8787d1fdfe2d92f8b2c9b959651d8d63be91b Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 4 Feb 2026 11:03:56 +0000 Subject: [PATCH 122/215] 8367530: The exhaustiveness errors could be improved Reviewed-by: vromero, mcimadamore --- .../javac/comp/ExhaustivenessComputer.java | 729 ++++++++++++++++-- .../com/sun/tools/javac/comp/Flow.java | 67 +- .../tools/javac/resources/compiler.properties | 22 +- .../javac/diags/examples/BindingPattern.java | 33 + .../diags/examples/EnumConstantPattern.java | 37 + .../javac/diags/examples/NotExhaustive.java | 3 +- .../examples/NotExhaustiveStatement.java | 3 +- .../javac/diags/examples/RecordPattern.java | 37 + .../tools/javac/patterns/Exhaustiveness.java | 3 +- .../ExhaustivenessConvenientErrors.java | 593 ++++++++++++++ .../PrimitiveInstanceOfComboTest.java | 5 +- .../PrimitivePatternsSwitchConstants.java | 2 +- .../PrimitivePatternsSwitchErrors.java | 2 +- .../tools/javac/patterns/SwitchErrors.java | 2 +- .../platform/NonExportedPermittedTypes.java | 8 +- .../ExpressionSwitchNotExhaustive.java | 2 +- 16 files changed, 1449 insertions(+), 99 deletions(-) create mode 100644 test/langtools/tools/javac/diags/examples/BindingPattern.java create mode 100644 test/langtools/tools/javac/diags/examples/EnumConstantPattern.java create mode 100644 test/langtools/tools/javac/diags/examples/RecordPattern.java create mode 100644 test/langtools/tools/javac/patterns/ExhaustivenessConvenientErrors.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java index bbc330832f3..2a0bab1e330 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.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 @@ -41,11 +41,17 @@ import com.sun.tools.javac.code.Kinds.Kind; import com.sun.tools.javac.code.Type.TypeVar; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.SequencedSet; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.util.stream.Collectors.groupingBy; +import static com.sun.tools.javac.code.Flags.RECORD; /** A class to compute exhaustiveness of set of switch cases. * @@ -55,6 +61,14 @@ import static java.util.stream.Collectors.groupingBy; * deletion without notice. */ public class ExhaustivenessComputer { + private static final long DEFAULT_MAX_BASE_CHECKS = 4_000_000; + + //when baseChecks is set to a value different that this, the checks + //will be counter, and if too many will happen, the process will be stopped + //when baseChecks is set to this value, there's no counting, and the + //process will not continue as long as needed + private static final long NO_BASE_CHECKS_COUNTING = -1; + protected static final Context.Key exhaustivenessKey = new Context.Key<>(); private final Symtab syms; @@ -62,6 +76,8 @@ public class ExhaustivenessComputer { private final Check chk; private final Infer infer; private final Map, Boolean> isSubtypeCache = new HashMap<>(); + private final long maxBaseChecks; + private long baseChecks = NO_BASE_CHECKS_COUNTING; public static ExhaustivenessComputer instance(Context context) { ExhaustivenessComputer instance = context.get(exhaustivenessKey); @@ -77,9 +93,22 @@ public class ExhaustivenessComputer { types = Types.instance(context); chk = Check.instance(context); infer = Infer.instance(context); + Options options = Options.instance(context); + String baseChecks = options.get("exhaustivityMaxBaseChecks"); + long computedMaxBaseChecks = DEFAULT_MAX_BASE_CHECKS; + + if (baseChecks != null) { + try { + computedMaxBaseChecks = Long.parseLong(baseChecks); + } catch (NumberFormatException _) { + //ignore invalid values and use the default maximum number of checks + } + } + + maxBaseChecks = computedMaxBaseChecks; } - public boolean exhausts(JCExpression selector, List cases) { + public ExhaustivenessResult exhausts(JCExpression selector, List cases) { Set patternSet = new HashSet<>(); Map> enum2Constants = new HashMap<>(); Set booleanLiterals = new HashSet<>(Set.of(0, 1)); @@ -113,7 +142,7 @@ public class ExhaustivenessComputer { } if (types.unboxedTypeOrType(selector.type).hasTag(TypeTag.BOOLEAN) && booleanLiterals.isEmpty()) { - return true; + return ExhaustivenessResult.ofExhaustive(); } for (Entry> e : enum2Constants.entrySet()) { @@ -121,47 +150,77 @@ public class ExhaustivenessComputer { patternSet.add(new BindingPattern(e.getKey().type)); } } - Set patterns = patternSet; - Set> seenFallback = new HashSet<>(); - boolean useHashes = true; try { - boolean repeat = true; - while (repeat) { - Set updatedPatterns; - updatedPatterns = reduceBindingPatterns(selector.type, patterns); - updatedPatterns = reduceNestedPatterns(updatedPatterns, useHashes); - updatedPatterns = reduceRecordPatterns(updatedPatterns); - updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); - repeat = !updatedPatterns.equals(patterns); - if (checkCovered(selector.type, patterns)) { - return true; - } - if (!repeat) { - //there may be situation like: - //class B permits S1, S2 - //patterns: R(S1, B), R(S2, S2) - //this might be joined to R(B, S2), as B could be rewritten to S2 - //but hashing in reduceNestedPatterns will not allow that - //disable the use of hashing, and use subtyping in - //reduceNestedPatterns to handle situations like this: - repeat = useHashes && seenFallback.add(updatedPatterns); - useHashes = false; - } else { - //if a reduction happened, make sure hashing in reduceNestedPatterns - //is enabled, as the hashing speeds up the process significantly: - useHashes = true; - } - patterns = updatedPatterns; + CoverageResult coveredResult = computeCoverage(selector.type, patternSet, PatternEquivalence.STRICT); + if (coveredResult.covered()) { + return ExhaustivenessResult.ofExhaustive(); } - return checkCovered(selector.type, patterns); + + Set details = + this.computeMissingPatternDescriptions(selector.type, coveredResult.incompletePatterns()) + .stream() + .flatMap(pd -> { + if (pd instanceof BindingPattern bp && enum2Constants.containsKey(bp.type.tsym)) { + Symbol enumType = bp.type.tsym; + return enum2Constants.get(enumType).stream().map(c -> new EnumConstantPattern(bp.type, c.name)); + } else { + return Stream.of(pd); + } + }) + .collect(Collectors.toSet()); + + return ExhaustivenessResult.ofDetails(details); } catch (CompletionFailure cf) { chk.completionError(selector.pos(), cf); - return true; //error recovery - } finally { - isSubtypeCache.clear(); + return ExhaustivenessResult.ofExhaustive(); //error recovery } } + /* Given the set of patterns, runs the reductions of it as long as possible. + * If the (reduced) set of patterns covers the given selector type, returns + * covered == true, and incompletePatterns == null. + * If the (reduced) set of patterns does not cover the given selector type, + * returns covered == false, and incompletePatterns == the reduced set of patterns. + */ + private CoverageResult computeCoverage(Type selectorType, Set patterns, PatternEquivalence patternEquivalence) { + Set updatedPatterns; + Set> seenPatterns = new HashSet<>(); + boolean useHashes = true; + boolean repeat = true; + do { + updatedPatterns = reduceBindingPatterns(selectorType, patterns); + updatedPatterns = reduceNestedPatterns(updatedPatterns, useHashes, patternEquivalence); + updatedPatterns = reduceRecordPatterns(updatedPatterns); + updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); + repeat = !updatedPatterns.equals(patterns); + if (checkCovered(selectorType, patterns)) { + return new CoverageResult(true, null); + } + if (!repeat) { + //there may be situation like: + //class B permits S1, S2 + //patterns: R(S1, B), R(S2, S2) + //this might be joined to R(B, S2), as B could be rewritten to S2 + //but hashing in reduceNestedPatterns will not allow that + //disable the use of hashing, and use subtyping in + //reduceNestedPatterns to handle situations like this: + repeat = useHashes && seenPatterns.add(updatedPatterns); + useHashes = false; + } else { + //if a reduction happened, make sure hashing in reduceNestedPatterns + //is enabled, as the hashing speeds up the process significantly: + useHashes = true; + } + patterns = updatedPatterns; + } while (repeat); + if (checkCovered(selectorType, patterns)) { + return new CoverageResult(true, null); + } + return new CoverageResult(false, patterns); + } + + private record CoverageResult(boolean covered, Set incompletePatterns) {} + private boolean checkCovered(Type seltype, Iterable patterns) { for (Type seltypeComponent : components(seltype)) { for (PatternDescription pd : patterns) { @@ -215,6 +274,7 @@ public class ExhaustivenessComputer { if (clazz.isSealed() && clazz.isAbstract() && //if a binding pattern for clazz already exists, no need to analyze it again: !existingBindings.contains(clazz)) { + ListBuffer bindings = new ListBuffer<>(); //do not reduce to types unrelated to the selector type: Type clazzType = clazz.type; if (components(selectorType).stream() @@ -222,16 +282,7 @@ public class ExhaustivenessComputer { continue; } - Set permitted = allPermittedSubTypes(clazz, csym -> { - Type instantiated; - if (csym.type.allparams().isEmpty()) { - instantiated = csym.type; - } else { - instantiated = infer.instantiatePatternType(selectorType, csym); - } - - return instantiated != null && types.isCastable(selectorType, instantiated); - }); + Set permitted = allPermittedSubTypes(clazz, isApplicableSubtypePredicate(selectorType)); //the set of pending permitted subtypes needed to cover clazz: Set pendingPermitted = new HashSet<>(permitted); @@ -263,7 +314,7 @@ public class ExhaustivenessComputer { } if (pendingPermitted.isEmpty()) { - toAdd.add(new BindingPattern(clazz.type)); + toAdd.add(new BindingPattern(clazz.type, Set.of())); } } } @@ -304,6 +355,49 @@ public class ExhaustivenessComputer { return permitted; } + private Predicate isApplicableSubtypePredicate(Type targetType) { + return csym -> { + Type instantiated = instantiatePatternType(targetType, csym); + + return instantiated != null && types.isCastable(targetType, instantiated); + }; + } + + private Type instantiatePatternType(Type targetType, TypeSymbol csym) { + if (csym.type.allparams().isEmpty()) { + return csym.type; + } else { + return infer.instantiatePatternType(targetType, csym); + } + } + + private Set leafPermittedSubTypes(TypeSymbol root, Predicate accept) { + Set permitted = new HashSet<>(); + List permittedSubtypesClosure = baseClasses(root); + + while (permittedSubtypesClosure.nonEmpty()) { + ClassSymbol current = permittedSubtypesClosure.head; + + permittedSubtypesClosure = permittedSubtypesClosure.tail; + + current.complete(); + + if (current.isSealed() && current.isAbstract()) { + for (Type t : current.getPermittedSubclasses()) { + ClassSymbol csym = (ClassSymbol) t.tsym; + + if (accept.test(csym)) { + permittedSubtypesClosure = permittedSubtypesClosure.prepend(csym); + } + } + } else { + permitted.add(current); + } + } + + return permitted; + } + private List baseClasses(TypeSymbol root) { if (root instanceof ClassSymbol clazz) { return List.of(clazz); @@ -336,7 +430,8 @@ public class ExhaustivenessComputer { * as pattern hashes cannot be used to speed up the matching process */ private Set reduceNestedPatterns(Set patterns, - boolean useHashes) { + boolean useHashes, + PatternEquivalence patternEquivalence) { /* implementation note: * finding a sub-set of patterns that only differ in a single * column is time-consuming task, so this method speeds it up by: @@ -386,13 +481,13 @@ public class ExhaustivenessComputer { RecordPattern rpOther = candidatesArr[nextCandidate]; if (rpOne.recordType.tsym == rpOther.recordType.tsym && - nestedComponentsEquivalent(rpOne, rpOther, mismatchingCandidate, useHashes)) { + nestedComponentsEquivalent(rpOne, rpOther, mismatchingCandidate, useHashes, patternEquivalence)) { join.append(rpOther); } } var nestedPatterns = join.stream().map(rp -> rp.nested[mismatchingCandidateFin]).collect(Collectors.toSet()); - var updatedPatterns = reduceNestedPatterns(nestedPatterns, useHashes); + var updatedPatterns = reduceNestedPatterns(nestedPatterns, useHashes, patternEquivalence); updatedPatterns = reduceRecordPatterns(updatedPatterns); updatedPatterns = removeCoveredRecordPatterns(updatedPatterns); @@ -403,16 +498,11 @@ public class ExhaustivenessComputer { current.removeAll(join); } - for (PatternDescription nested : updatedPatterns) { - PatternDescription[] newNested = - Arrays.copyOf(rpOne.nested, rpOne.nested.length); - newNested[mismatchingCandidateFin] = nested; - RecordPattern nue = new RecordPattern(rpOne.recordType(), - rpOne.fullComponentTypes(), - newNested, - new HashSet<>(join)); - current.add(nue); - } + generatePatternsWithReplacedNestedPattern(rpOne, + mismatchingCandidateFin, + updatedPatterns, + Set.copyOf(join), + current::add); } } } @@ -434,11 +524,32 @@ public class ExhaustivenessComputer { * - it's type is a supertype of the existing pattern's type * - it was produced by a reduction from a record pattern that is equivalent to * the existing pattern + * - only if PatternEquivalence is LOOSE and the type is the same of the type + * of an existing record pattern (the binding pattern may stand in place of + * a record pattern). This is only used to compute the missing patterns that + * would make the original pattern set exhaustive. + * + * For example, having (with mismatchingCandidate == 0): + * existing: R(A _, Box(var _)) {} + * cadidate: R(B _, Box(var _)) {} + * these are always equivalent; as all nested patterns except of + * component 0 are exactly equivalent + * + * existing: R(A _, SubtypeOfBox _) {} + * cadidate: R(A _, Box _) {} + * this is only equivalent when useHashes == false; Box _ could be replaced + * with a more specific SubtypeOfBox _ + * + * existing: R(A _, Box(var _)) {} + * cadidate: R(A _, Box _) {} + * this is only equivalent when useHashes == false and patternEquivalence == LOOSE; + * Box _ is accepted in place of the more specific record pattern */ private boolean nestedComponentsEquivalent(RecordPattern existing, RecordPattern candidate, int mismatchingCandidate, - boolean useHashes) { + boolean useHashes, + PatternEquivalence patternEquivalence) { NEXT_NESTED: for (int i = 0; i < existing.nested.length; i++) { if (i != mismatchingCandidate) { @@ -457,22 +568,28 @@ public class ExhaustivenessComputer { return false; } } else if (existing.nested[i] instanceof RecordPattern nestedExisting) { - java.util.List pendingReplacedPatterns = - new ArrayList<>(nestedCandidate.sourcePatterns()); + if (patternEquivalence == PatternEquivalence.LOOSE) { + if (!isSubtypeErasure(nestedExisting.recordType(), nestedCandidate.type)) { + return false; + } + } else { + java.util.List pendingReplacedPatterns = + new ArrayList<>(nestedCandidate.sourcePatterns()); - while (!pendingReplacedPatterns.isEmpty()) { - PatternDescription currentReplaced = pendingReplacedPatterns.removeLast(); + while (!pendingReplacedPatterns.isEmpty()) { + PatternDescription currentReplaced = pendingReplacedPatterns.removeLast(); - if (nestedExisting.equals(currentReplaced)) { - //candidate.nested[i] is substitutable for existing.nested[i] - //continue with the next nested pattern: - continue NEXT_NESTED; + if (nestedExisting.equals(currentReplaced)) { + //candidate.nested[i] is substitutable for existing.nested[i] + //continue with the next nested pattern: + continue NEXT_NESTED; + } + + pendingReplacedPatterns.addAll(currentReplaced.sourcePatterns()); } - pendingReplacedPatterns.addAll(currentReplaced.sourcePatterns()); + return false; } - - return false; } else { return false; } @@ -563,6 +680,8 @@ public class ExhaustivenessComputer { } private boolean isBpCovered(Type componentType, PatternDescription newNested) { + reportCheck(); + if (newNested instanceof BindingPattern bp) { Type seltype = types.erasure(componentType); Type pattype = types.erasure(bp.type); @@ -574,9 +693,18 @@ public class ExhaustivenessComputer { return false; } - sealed interface PatternDescription { + protected void reportCheck() { + if (baseChecks != NO_BASE_CHECKS_COUNTING && + ++baseChecks > maxBaseChecks) { + throw new TooManyChecksException(null); + } + } + + public sealed interface PatternDescription { + public Type type(); public Set sourcePatterns(); } + public PatternDescription makePatternDescription(Type selectorType, JCPattern pattern) { if (pattern instanceof JCBindingPattern binding) { Type type = !selectorType.isPrimitive() && types.isSubtype(selectorType, binding.type) @@ -586,9 +714,7 @@ public class ExhaustivenessComputer { Type[] componentTypes; if (!record.type.isErroneous()) { - componentTypes = ((ClassSymbol) record.type.tsym).getRecordComponents() - .map(r -> types.memberType(record.type, r)) - .toArray(s -> new Type[s]); + componentTypes = instantiatedComponentTypes(record.type); } else { componentTypes = record.nested.map(t -> types.createErrorType(t.type)).toArray(s -> new Type[s]);; @@ -611,7 +737,7 @@ public class ExhaustivenessComputer { throw Assert.error(); } } - record BindingPattern(Type type, Set sourcePatterns) implements PatternDescription { + public record BindingPattern(Type type, Set sourcePatterns) implements PatternDescription { public BindingPattern(Type type) { this(type, Set.of()); @@ -631,7 +757,7 @@ public class ExhaustivenessComputer { return type.tsym + " _"; } } - record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription[] nested, Set sourcePatterns) implements PatternDescription { + public record RecordPattern(Type recordType, int _hashCode, Type[] fullComponentTypes, PatternDescription[] nested, Set sourcePatterns) implements PatternDescription { public RecordPattern(Type recordType, Type[] fullComponentTypes, PatternDescription[] nested) { this(recordType, fullComponentTypes, nested, Set.of()); @@ -673,5 +799,450 @@ public class ExhaustivenessComputer { .map(pd -> pd.toString()) .collect(Collectors.joining(", ")) + ")"; } + + @Override + public Type type() { + return recordType; + } + } + + public record EnumConstantPattern(Type enumType, Name enumConstant) implements PatternDescription { + + @Override + public Type type() { + return enumType(); + } + + @Override + public Set sourcePatterns() { + return Set.of(); + } + public String toString() { + return enumType() + "." + enumConstant(); + } + } + + public record ExhaustivenessResult(boolean exhaustive, Set notExhaustiveDetails) { + public static ExhaustivenessResult ofExhaustive() { + return new ExhaustivenessResult(true, null); + } + public static ExhaustivenessResult ofDetails(Set notExhaustiveDetails) { + return new ExhaustivenessResult(false, notExhaustiveDetails != null ? notExhaustiveDetails : Set.of()); + } + } + + //computation of missing patterns: + protected Set computeMissingPatternDescriptions(Type selectorType, + Set incompletePatterns) { + if (maxBaseChecks == 0) { + return Set.of(); + } + try { + baseChecks = 0; + PatternDescription defaultPattern = new BindingPattern(selectorType); + return expandMissingPatternDescriptions(selectorType, + selectorType, + defaultPattern, + incompletePatterns, + Set.of(defaultPattern)); + } catch (TooManyChecksException ex) { + return ex.missingPatterns != null ? ex.missingPatterns : Set.of(); + } finally { + baseChecks = NO_BASE_CHECKS_COUNTING; + } + } + + private Set expandMissingPatternDescriptions(Type selectorType, + Type targetType, + PatternDescription toExpand, + Set basePatterns, + Set inMissingPatterns) { + try { + return doExpandMissingPatternDescriptions(selectorType, targetType, + toExpand, basePatterns, + inMissingPatterns); + } catch (TooManyChecksException ex) { + if (ex.missingPatterns == null) { + ex = new TooManyChecksException(inMissingPatterns); + } + throw ex; + } + } + + private Set doExpandMissingPatternDescriptions(Type selectorType, + Type targetType, + PatternDescription toExpand, + Set basePatterns, + Set inMissingPatterns) { + if (toExpand instanceof BindingPattern bp) { + if (bp.type.tsym.isSealed()) { + //try to replace binding patterns for sealed types with all their immediate permitted applicable types: + List permitted = ((ClassSymbol) bp.type.tsym).getPermittedSubclasses(); + Set applicableDirectPermittedPatterns = + permitted.stream() + .map(type -> type.tsym) + .filter(isApplicableSubtypePredicate(targetType)) + .map(csym -> new BindingPattern(types.erasure(csym.type))) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + //remove the permitted subtypes that are not needed to achieve exhaustivity + boolean reduced = + removeUnnecessaryPatterns(selectorType, bp, basePatterns, inMissingPatterns, applicableDirectPermittedPatterns); + + if (!reduced && !hasMatchingRecordPattern(basePatterns, inMissingPatterns, toExpand)) { + //if all immediate permitted subtypes are needed + //give up, and simply use the current pattern: + return inMissingPatterns; + } + + Set currentMissingPatterns = + replace(inMissingPatterns, toExpand, applicableDirectPermittedPatterns); + + //try to recursively expand on each viable pattern: + for (PatternDescription viable : applicableDirectPermittedPatterns) { + currentMissingPatterns = expandMissingPatternDescriptions(selectorType, targetType, + viable, basePatterns, + currentMissingPatterns); + } + + return currentMissingPatterns; + } else if ((bp.type.tsym.flags_field & Flags.RECORD) != 0 && + //only expand record types into record patterns if there's a chance it may change the outcome + //i.e. there is a record pattern in at the spot in the original base patterns: + hasMatchingRecordPattern(basePatterns, inMissingPatterns, toExpand)) { + //if there is a binding pattern at a place where the original based patterns + //have a record pattern, try to expand the binding pattern into a record pattern + //create all possible combinations of record pattern components: + Type[] componentTypes = instantiatedComponentTypes(bp.type); + List> combinatorialNestedTypes = List.of(List.nil()); + + for (Type componentType : componentTypes) { + List applicableLeafPermittedSubtypes; + + if (componentType.tsym.isSealed()) { + applicableLeafPermittedSubtypes = + leafPermittedSubTypes(componentType.tsym, + isApplicableSubtypePredicate(componentType)) + .stream() + .map(csym -> instantiatePatternType(componentType, csym)) + .collect(List.collector()); + } else { + applicableLeafPermittedSubtypes = List.of(componentType); + } + + List> newCombinatorialNestedTypes = List.nil(); + + for (List existing : combinatorialNestedTypes) { + for (Type nue : applicableLeafPermittedSubtypes) { + newCombinatorialNestedTypes = newCombinatorialNestedTypes.prepend(existing.append(nue)); + } + } + + combinatorialNestedTypes = newCombinatorialNestedTypes; + } + + Set combinatorialPatterns = + combinatorialNestedTypes.stream() + .map(combination -> new RecordPattern(bp.type, + componentTypes, + combination.map(BindingPattern::new) + .toArray(PatternDescription[]::new))) + .collect(Collectors.toCollection(LinkedHashSet::new)); + + removeUnnecessaryPatterns(selectorType, bp, basePatterns, inMissingPatterns, combinatorialPatterns); + + CoverageResult coverageResult = computeCoverage(targetType, combinatorialPatterns, PatternEquivalence.LOOSE); + + if (!coverageResult.covered()) { + //use the partially merged/combined patterns: + combinatorialPatterns = coverageResult.incompletePatterns(); + } + + //combine sealed subtypes into the supertype, if all is covered. + //but preserve more specific record types in positions where there are record patterns in the original patterns + //this is particularly important for the case where the sealed supertype only has one permitted type, the record + //the base type could be used instead of the record otherwise, which would produce less specific missing pattern: + Set sortedCandidates = + partialSortPattern(combinatorialPatterns, basePatterns, replace(inMissingPatterns, toExpand, combinatorialPatterns)); + + removeUnnecessaryPatterns(selectorType, bp, basePatterns, inMissingPatterns, sortedCandidates); + + Set currentMissingPatterns = + replace(inMissingPatterns, toExpand, sortedCandidates); + + for (PatternDescription addedPattern : sortedCandidates) { + if (addedPattern instanceof RecordPattern addedRP) { + for (int c = 0; c < addedRP.nested.length; c++) { + currentMissingPatterns = expandMissingPatternDescriptions(selectorType, + addedRP.fullComponentTypes[c], + addedRP.nested[c], + basePatterns, + currentMissingPatterns); + } + } + } + + return currentMissingPatterns; + } + } + return inMissingPatterns; + } + + /* + * Inside any pattern in {@code in}, in any nesting depth, replace + * pattern {@code what} with patterns {@code to}. + */ + private Set replace(Iterable in, + PatternDescription what, + Collection to) { + Set result = new HashSet<>(); + + for (PatternDescription pd : in) { + Collection replaced = replace(pd, what, to); + if (replaced != null) { + result.addAll(replaced); + } else { + result.add(pd); + } + } + + return result; + } + //where: + //null: no change + private Collection replace(PatternDescription in, + PatternDescription what, + Collection to) { + if (in == what) { + return to; + } else if (in instanceof RecordPattern rp) { + for (int c = 0; c < rp.nested.length; c++) { + Collection replaced = replace(rp.nested[c], what, to); + if (replaced != null) { + Set withReplaced = new HashSet<>(); + + generatePatternsWithReplacedNestedPattern(rp, c, replaced, Set.of(), withReplaced::add); + + return replace(withReplaced, what, to); + } + } + return null; + } else { + return null; //binding patterns have no children + } + } + + /* Out of "candidates" remove patterns that are not necessary to achieve exhaustiveness. + * Note that iteration order of "candidates" is important - if the set contains + * two pattern, out of which either, but not both, is needed to achieve exhaustiveness, + * the first one in the iteration order will be removed. + */ + private boolean removeUnnecessaryPatterns(Type selectorType, + PatternDescription toExpand, + Set basePatterns, + Set inMissingPatterns, + Set candidates) { + boolean reduced = false; + + for (Iterator it = candidates.iterator(); it.hasNext(); ) { + PatternDescription current = it.next(); + Set reducedAdded = new HashSet<>(candidates); + + reducedAdded.remove(current); + + Set combinedPatterns = + Stream.concat(basePatterns.stream(), + replace(inMissingPatterns, toExpand, reducedAdded).stream()) + .collect(Collectors.toSet()); + + if (computeCoverage(selectorType, combinedPatterns, PatternEquivalence.LOOSE).covered()) { + it.remove(); + reduced = true; + } + } + + return reduced; + } + /* + * Sort patterns so that those that are preferred for removal are in front + * of those that are preferred to remain (when there's a choice). + */ + private SequencedSet partialSortPattern(Set candidates, + Set basePatterns, + Set missingPatterns) { + SequencedSet sortedCandidates = new LinkedHashSet<>(); + + while (!candidates.isEmpty()) { + PatternDescription mostSpecific = null; + for (PatternDescription current : candidates) { + if (mostSpecific == null || + shouldAppearBefore(current, mostSpecific, basePatterns, missingPatterns)) { + mostSpecific = current; + } + } + sortedCandidates.add(mostSpecific); + candidates.remove(mostSpecific); + } + return sortedCandidates; + } + //where: + //true iff pd1 should appear before pd2 + //false otherwise + private boolean shouldAppearBefore(PatternDescription pd1, + PatternDescription pd2, + Set basePatterns, + Set missingPatterns) { + if (pd1 instanceof RecordPattern rp1 && pd2 instanceof RecordPattern rp2) { + for (int c = 0; c < rp1.nested.length; c++) { + if (shouldAppearBefore((BindingPattern) rp1.nested[c], + (BindingPattern) rp2.nested[c], + basePatterns, + missingPatterns)) { + return true; + } + } + } else if (pd1 instanceof BindingPattern bp1 && pd2 instanceof BindingPattern bp2) { + Type t1 = bp1.type(); + Type t2 = bp2.type(); + boolean t1IsImportantRecord = + (t1.tsym.flags_field & RECORD) != 0 && + hasMatchingRecordPattern(basePatterns, missingPatterns, bp1); + boolean t2IsImportantRecord = + (t2.tsym.flags_field & RECORD) != 0 && + hasMatchingRecordPattern(basePatterns, missingPatterns, bp2); + if (t1IsImportantRecord && !t2IsImportantRecord) { + return false; + } + if (!t1IsImportantRecord && t2IsImportantRecord) { + return true; + } + if (!types.isSameType(t1, t2) && types.isSubtype(t1, t2)) { + return true; + } + } + + return false; + } + + /* + * Do the {@code basePatterns} have a record pattern at a place that corresponds to + * position of pattern {@code query} inside {@code missingPatterns}? + */ + private boolean hasMatchingRecordPattern(Set basePatterns, + Set missingPatterns, + PatternDescription query) { + PatternDescription root = findRootContaining(missingPatterns, query); + + if (root == null) { + return false; + } + return basePatternsHaveRecordPatternOnThisSpot(basePatterns, root, query); + } + //where: + private PatternDescription findRootContaining(Set rootPatterns, + PatternDescription added) { + for (PatternDescription pd : rootPatterns) { + if (isUnderRoot(pd, added)) { + return pd; + } + } + + return null; + } + + private boolean basePatternsHaveRecordPatternOnThisSpot(Set basePatterns, + PatternDescription rootPattern, + PatternDescription added) { + if (rootPattern == added) { + return basePatterns.stream().anyMatch(pd -> pd instanceof RecordPattern); + } + if (!(rootPattern instanceof RecordPattern rootPatternRecord)) { + return false; + } + int index = -1; + for (int c = 0; c < rootPatternRecord.nested.length; c++) { + if (isUnderRoot(rootPatternRecord.nested[c], added)) { + index = c; + break; + } + } + // 'index' must be one of rootPatternRecord.nested; if not, `isUnderRoot` is inconsistent. + Assert.check(index != (-1)); + + int indexFin = index; + Set filteredBasePatterns = + basePatterns.stream() + .filter(pd -> pd instanceof RecordPattern) + .map(rp -> (RecordPattern) rp) + .filter(rp -> types.isSameType(rp.recordType(), rootPatternRecord.recordType())) + .map(rp -> rp.nested[indexFin]) + .collect(Collectors.toSet()); + + return basePatternsHaveRecordPatternOnThisSpot(filteredBasePatterns, rootPatternRecord.nested[index], added); + } + + private boolean isUnderRoot(PatternDescription root, PatternDescription searchFor) { + if (root == searchFor) { + return true; + } else if (root instanceof RecordPattern rp) { + for (int c = 0; c < rp.nested.length; c++) { + if (isUnderRoot(rp.nested[c], searchFor)) { + return true; + } + } + } + return false; + } + + /* + * Using {@code basePattern} as a starting point, generate new {@code + * RecordPattern}s, such that all corresponding components but one, are the + * same. The component described by the {@code replaceComponent} index is + * replaced with all {@code PatternDescription}s taken from {@code + * updatedNestedPatterns} and the resulting {@code RecordPatterns}s are sent + * to {@code target}. + */ + private void generatePatternsWithReplacedNestedPattern(RecordPattern basePattern, + int replaceComponent, + Iterable updatedNestedPatterns, + Set sourcePatterns, + Consumer target) { + for (PatternDescription nested : updatedNestedPatterns) { + PatternDescription[] newNested = + Arrays.copyOf(basePattern.nested, basePattern.nested.length); + newNested[replaceComponent] = nested; + target.accept(new RecordPattern(basePattern.recordType(), + basePattern.fullComponentTypes(), + newNested, + sourcePatterns)); + } + } + + /* For a given record type, return the record's component types, with their + * types instatiated according to the exact record type. + */ + private Type[] instantiatedComponentTypes(Type recordType) { + Type[] componentTypes = ((ClassSymbol) recordType.tsym).getRecordComponents() + .map(r -> types.memberType(recordType, r)) + .toArray(s -> new Type[s]); + return componentTypes; + } + + /* The strictness of determining the equivalent of patterns, used in + * nestedComponentsEquivalent. + */ + private enum PatternEquivalence { + STRICT, + LOOSE; + } + + protected static class TooManyChecksException extends RuntimeException { + private static final long serialVersionUID = 0L; + private transient final Set missingPatterns; + + public TooManyChecksException(Set missingPatterns) { + super(null, null, false, false); + this.missingPatterns = missingPatterns; + } } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index e74aed6a357..cbcb474a37f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.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 @@ -50,9 +50,15 @@ import static com.sun.tools.javac.code.Flags.BLOCK; import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.TypeTag.BOOLEAN; import static com.sun.tools.javac.code.TypeTag.VOID; +import com.sun.tools.javac.comp.ExhaustivenessComputer.BindingPattern; +import com.sun.tools.javac.comp.ExhaustivenessComputer.EnumConstantPattern; +import com.sun.tools.javac.comp.ExhaustivenessComputer.ExhaustivenessResult; +import com.sun.tools.javac.comp.ExhaustivenessComputer.PatternDescription; +import com.sun.tools.javac.comp.ExhaustivenessComputer.RecordPattern; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import static com.sun.tools.javac.tree.JCTree.Tag.*; import com.sun.tools.javac.util.JCDiagnostic.Fragment; +import java.util.Arrays; /** This pass implements dataflow analysis for Java programs though * different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that @@ -696,9 +702,18 @@ public class Flow { tree.isExhaustive = tree.hasUnconditionalPattern || TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases); if (exhaustiveSwitch) { - tree.isExhaustive |= exhaustiveness.exhausts(tree.selector, tree.cases); if (!tree.isExhaustive) { - log.error(tree, Errors.NotExhaustiveStatement); + ExhaustivenessResult exhaustivenessResult = exhaustiveness.exhausts(tree.selector, tree.cases); + + tree.isExhaustive = exhaustivenessResult.exhaustive(); + + if (!tree.isExhaustive) { + if (exhaustivenessResult.notExhaustiveDetails().isEmpty()) { + log.error(tree, Errors.NotExhaustiveStatement); + } else { + logNotExhaustiveError(tree.pos(), exhaustivenessResult, Errors.NotExhaustiveStatementDetails); + } + } } } if (!tree.hasUnconditionalPattern && !exhaustiveSwitch) { @@ -735,16 +750,54 @@ public class Flow { TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases)) { tree.isExhaustive = true; } else { - tree.isExhaustive = exhaustiveness.exhausts(tree.selector, tree.cases); + ExhaustivenessResult exhaustivenessResult = exhaustiveness.exhausts(tree.selector, tree.cases); + + tree.isExhaustive = exhaustivenessResult.exhaustive(); + + if (!tree.isExhaustive) { + if (exhaustivenessResult.notExhaustiveDetails().isEmpty()) { + log.error(tree, Errors.NotExhaustive); + } else { + logNotExhaustiveError(tree.pos(), exhaustivenessResult, Errors.NotExhaustiveDetails); + } + } } - if (!tree.isExhaustive) { - log.error(tree, Errors.NotExhaustive); - } alive = prevAlive; alive = alive.or(resolveYields(tree, prevPendingExits)); } + private void logNotExhaustiveError(DiagnosticPosition pos, + ExhaustivenessResult exhaustivenessResult, + Error errorKey) { + List details = + exhaustivenessResult.notExhaustiveDetails() + .stream() + .map(this::patternToDiagnostic) + .sorted((d1, d2) -> d1.toString() + .compareTo(d2.toString())) + .collect(List.collector()); + JCDiagnostic main = diags.error(null, log.currentSource(), pos, errorKey); + JCDiagnostic d = new JCDiagnostic.MultilineDiagnostic(main, details); + log.report(d); + } + + private JCDiagnostic patternToDiagnostic(PatternDescription desc) { + Type patternType = types.erasure(desc.type()); + return diags.fragment(switch (desc) { + case BindingPattern _ -> + Fragments.BindingPattern(patternType); + case RecordPattern rp -> + Fragments.RecordPattern(patternType, + Arrays.stream(rp.nested()) + .map(this::patternToDiagnostic) + .toList()); + case EnumConstantPattern ep -> + Fragments.EnumConstantPattern(patternType, + ep.enumConstant()); + }); + } + public void visitTry(JCTry tree) { ListBuffer prevPendingExits = pendingExits; pendingExits = new ListBuffer<>(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index 2ba9122c04a..bb81916becb 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -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 @@ -1476,6 +1476,26 @@ compiler.err.not.exhaustive=\ compiler.err.not.exhaustive.statement=\ the switch statement does not cover all possible input values +compiler.err.not.exhaustive.details=\ + the switch expression does not cover all possible input values\n\ + missing patterns: + +compiler.err.not.exhaustive.statement.details=\ + the switch statement does not cover all possible input values\n\ + missing patterns: + +# 0: type +compiler.misc.binding.pattern=\ + {0} _ + +# 0: type, 1: list of diagnostic +compiler.misc.record.pattern=\ + {0}({1}) + +# 0: type, 1: name +compiler.misc.enum.constant.pattern=\ + {0}.{1} + compiler.err.initializer.must.be.able.to.complete.normally=\ initializer must be able to complete normally diff --git a/test/langtools/tools/javac/diags/examples/BindingPattern.java b/test/langtools/tools/javac/diags/examples/BindingPattern.java new file mode 100644 index 00000000000..01913a47fd7 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/BindingPattern.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +// key: compiler.err.not.exhaustive.details +// key: compiler.misc.binding.pattern + +class BindingPattern { + int t(Object o) { + return switch (o) { + case String s -> 0; + }; + } +} diff --git a/test/langtools/tools/javac/diags/examples/EnumConstantPattern.java b/test/langtools/tools/javac/diags/examples/EnumConstantPattern.java new file mode 100644 index 00000000000..b2ab5b9028d --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/EnumConstantPattern.java @@ -0,0 +1,37 @@ +/* + * 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. + */ + +// key: compiler.err.not.exhaustive.details +// key: compiler.misc.enum.constant.pattern + +class NotExhaustiveDetails { + int t(I i) { + return switch (i) { + case R r -> -1; + case E.A -> -1; + }; + } + sealed interface I {} + enum E implements I {A, B} + record R(E e) implements I {} +} diff --git a/test/langtools/tools/javac/diags/examples/NotExhaustive.java b/test/langtools/tools/javac/diags/examples/NotExhaustive.java index 8d36b013677..f048755ec79 100644 --- a/test/langtools/tools/javac/diags/examples/NotExhaustive.java +++ b/test/langtools/tools/javac/diags/examples/NotExhaustive.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, 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,6 +22,7 @@ */ // key: compiler.err.not.exhaustive +// options: -XDexhaustivityMaxBaseChecks=0 class NotExhaustive { int t(int i) { diff --git a/test/langtools/tools/javac/diags/examples/NotExhaustiveStatement.java b/test/langtools/tools/javac/diags/examples/NotExhaustiveStatement.java index 65a11abb0e6..49e4955cbdb 100644 --- a/test/langtools/tools/javac/diags/examples/NotExhaustiveStatement.java +++ b/test/langtools/tools/javac/diags/examples/NotExhaustiveStatement.java @@ -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 @@ -22,6 +22,7 @@ */ // key: compiler.err.not.exhaustive.statement +// options: -XDexhaustivityMaxBaseChecks=0 class NotExhaustive { void t(Object o) { diff --git a/test/langtools/tools/javac/diags/examples/RecordPattern.java b/test/langtools/tools/javac/diags/examples/RecordPattern.java new file mode 100644 index 00000000000..2bb43bfd800 --- /dev/null +++ b/test/langtools/tools/javac/diags/examples/RecordPattern.java @@ -0,0 +1,37 @@ +/* + * 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. + */ + +// key: compiler.err.not.exhaustive.statement.details +// key: compiler.misc.record.pattern + +class RecordPattern { + void t(R r) { + switch (r) { + case R(C1 _) -> {} + }; + } + sealed interface I {} + record C1() implements I {} + record C2() implements I {} + record R(I i) {} +} diff --git a/test/langtools/tools/javac/patterns/Exhaustiveness.java b/test/langtools/tools/javac/patterns/Exhaustiveness.java index 84e67855b3b..f1809e99e45 100644 --- a/test/langtools/tools/javac/patterns/Exhaustiveness.java +++ b/test/langtools/tools/javac/patterns/Exhaustiveness.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 @@ -2523,6 +2523,7 @@ public class Exhaustiveness extends TestRunner { "-Xlint:-preview", "--class-path", libClasses.toString(), "-XDshould-stop.at=FLOW", + "-XDexhaustivityMaxBaseChecks=0", stopAtFlow ? "-XDshould-stop.ifNoError=FLOW" : "-XDnoop") .outdir(classes) diff --git a/test/langtools/tools/javac/patterns/ExhaustivenessConvenientErrors.java b/test/langtools/tools/javac/patterns/ExhaustivenessConvenientErrors.java new file mode 100644 index 00000000000..6935fcfa006 --- /dev/null +++ b/test/langtools/tools/javac/patterns/ExhaustivenessConvenientErrors.java @@ -0,0 +1,593 @@ +/* + * 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 8367530 + * @summary Check enhanced exhaustiveness errors + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @run main ExhaustivenessConvenientErrors +*/ + +import com.sun.tools.javac.api.ClientCodeWrapper.DiagnosticSourceUnwrapper; +import com.sun.tools.javac.util.JCDiagnostic; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.TestRunner; +import toolbox.ToolBox; + +public class ExhaustivenessConvenientErrors extends TestRunner { + + ToolBox tb; + + public static void main(String... args) throws Exception { + new ExhaustivenessConvenientErrors().runTests(); + } + + ExhaustivenessConvenientErrors() { + super(System.err); + tb = new ToolBox(); + } + + public void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void testExhaustiveSealedClasses(Path base) throws Exception { + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public final class A implements S {} + """, + """ + package lib; + public final class B implements S {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(S obj) { + return switch (obj) { + case A a -> 0; + }; + } + } + """, + "lib.B _"); + } + + @Test + public void testExhaustiveSealedClassesTransitive(Path base) throws Exception { + doTest(base, + new String[]{""" + package lib; + public sealed interface S1 permits S2, A {} + """, + """ + package lib; + public sealed interface S2 extends S1 permits S3, B {} + """, + """ + package lib; + public sealed interface S3 extends S2 permits C, D {} + """, + """ + package lib; + public final class A implements S1 {} + """, + """ + package lib; + public final class B implements S2 {} + """, + """ + package lib; + public final class C implements S3 {} + """, + """ + package lib; + public final class D implements S3 {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(S1 obj) { + return switch (obj) { + case A a -> 0; + case B a -> 0; + case D a -> 0; + }; + } + } + """, + "lib.C _"); + } + + @Test + public void testTrivialRecord(Path base) throws Exception { + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public final class A implements S {} + """, + """ + package lib; + public final class B implements S {} + """, + """ + package lib; + public record R(S s) {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(R r) { + return switch (r) { + case R(A a) -> 0; + }; + } + } + """, + "lib.R(lib.B _)"); + } + + @Test + public void testNonNestedRecord(Path base) throws Exception { + doTest(base, + new String[]{""" + package lib; + public sealed interface S permits A, B {} + """, + """ + package lib; + public final class A implements S {} + """, + """ + package lib; + public final class B implements S {} + """, + """ + package lib; + public record R(S s1, S s2) {} + """}, + """ + package test; + import lib.*; + public class Test { + private int test(R r) { + return switch (r) { + case R(A a, B b) -> 0; + case R(B b, A a) -> 0; + }; + } + } + """, + "lib.R(lib.A _,lib.A _)", + "lib.R(lib.B _,lib.B _)"); + } + + @Test + public void testComplex1(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + import lib.*; + public class Test { + private int test(Root r) { + return switch (r) { + case Root(R1 _, _, _) -> 0; + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2() implements Base {} + record R3(Base b1, Base b2) implements Base {} + record Root(Base b1, Base b2, Base b3) {} + } + """, + "test.Test.Root(test.Test.R2 _,test.Test.Base _,test.Test.Base _)", + "test.Test.Root(test.Test.R3 _,test.Test.Base _,test.Test.Base _)"); + } + + @Test + public void testComplex2(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + import lib.*; + public class Test { + private int test(Root r) { + return switch (r) { + case Root(R1 _, _, _) -> 0; + case Root(R2 _, R1 _, _) -> 0; + case Root(R2 _, R2 _, R1 _) -> 0; + case Root(R2 _, R2(R1 _, R1 _), R2(R1 _, R1 _)) -> 0; + case Root(R2 _, R2(R1 _, R1 _), R2(R1 _, R2 _)) -> 0; + case Root(R2 _, R2(R1 _, R1 _), R2(R2 _, R1 _)) -> 0; + case Root(R2 _, R2(R1 _, R1 _), R2(R2 _, R2 _)) -> 0; + case Root(R2 _, R2(R1 _, R2 _), R2(R1 _, R1 _)) -> 0; + case Root(R2 _, R2(R1 _, R2 _), R2(R1 _, R2 _)) -> 0; + case Root(R2 _, R2(R1 _, R2 _), R2(R2 _, R1 _)) -> 0; + case Root(R2 _, R2(R1 _, R2 _), R2(R2 _, R2 _)) -> 0; + case Root(R2 _, R2(R2 _, R1 _), R2(R1 _, R1 _)) -> 0; + case Root(R2 _, R2(R2 _, R1 _), R2(R1 _, R2 _)) -> 0; + case Root(R2 _, R2(R2 _, R1 _), R2(R2 _, R1 _)) -> 0; + case Root(R2 _, R2(R2 _, R1 _), R2(R2 _, R2 _)) -> 0; + case Root(R2 _, R2(R2 _, R2 _), R2(R1 _, R1 _)) -> 0; + case Root(R2 _, R2(R2 _, R2 _), R2(R1 _, R2 _)) -> 0; + case Root(R2 _, R2(R2 _, R2 _), R2(R2 _, R1 _)) -> 0; +// case Root(R2 _, R2(R2 _, R2 _), R2(R2 _, R2 _)) -> 0; + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2(Base b1, Base b2) implements Base {} + record Root(Base b1, Base b2, Base b3) {} + } + """, + "test.Test.Root(test.Test.R2 _,test.Test.R2(test.Test.R2 _,test.Test.R2 _),test.Test.R2(test.Test.R2 _,test.Test.R2 _))"); + } + + @Test + public void testComplex3(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test(Triple p) { + return switch (p) { + case Triple(B _, _, _) -> 0; + case Triple(_, A _, _) -> 0; + case Triple(_, _, A _) -> 0; + case Triple(A p, C(Nested _, NestedBaseA _), _) -> 0; + case Triple(A p, C(Nested _, NestedBaseB _), C(Nested _, NestedBaseA _)) -> 0; + case Triple(A p, C(Nested _, NestedBaseB _), C(Nested _, NestedBaseB _)) -> 0; + case Triple(A p, C(Nested _, NestedBaseB _), C(Nested _, NestedBaseC _)) -> 0; + case Triple(A p, C(Nested _, NestedBaseC _), C(Nested _, NestedBaseA _)) -> 0; + case Triple(A p, C(Nested _, NestedBaseC _), C(Nested _, NestedBaseB _)) -> 0; +// case Path(A p, C(Nested _, NestedBaseC _), C(Nested _, NestedBaseC _)) -> 0; + }; + } + record Triple(Base c1, Base c2, Base c3) {} + sealed interface Base permits A, B {} + record A(boolean key) implements Base { + } + sealed interface B extends Base {} + record C(Nested n, NestedBase b) implements B {} + record Nested() {} + sealed interface NestedBase {} + record NestedBaseA() implements NestedBase {} + record NestedBaseB() implements NestedBase {} + record NestedBaseC() implements NestedBase {} + } + """, + "test.Test.Triple(test.Test.A _,test.Test.C(test.Test.Nested _,test.Test.NestedBaseC _),test.Test.C(test.Test.Nested _,test.Test.NestedBaseC _))"); + } + + @Test + public void testComplex4(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + import lib.*; + public class Test { + private int test(Root r) { + return switch (r) { + case Root(R1 _, _, _) -> 0; + case Root(R2 _, R1 _, _) -> 0; + case Root(R2 _, R2 _, R1 _) -> 0; + case Root(R2 _, R2(R1 _, R1 _), R2(R1 _, R1 _)) -> 0; + case Root(R2 _, R2(R1 _, R1 _), R2(R1 _, R2 _)) -> 0; + case Root(R2 _, R2(R1 _, R1 _), R2(R2 _, R1 _)) -> 0; + case Root(R2 _, R2(R1 _, R1 _), R2(R2 _, R2 _)) -> 0; + case Root(R2 _, R2(R1 _, R2 _), R2(R1 _, R1 _)) -> 0; + case Root(R2 _, R2(R1 _, R2 _), R2(R1 _, R2 _)) -> 0; +// case Root(R2 _, R2(R1 _, R2 _), R2(R2 _, R1 _)) -> 0; + case Root(R2 _, R2(R1 _, R2 _), R2(R2 _, R2 _)) -> 0; + case Root(R2 _, R2(R2 _, R1 _), R2(R1 _, R1 _)) -> 0; + case Root(R2 _, R2(R2 _, R1 _), R2(R1 _, R2 _)) -> 0; + case Root(R2 _, R2(R2 _, R1 _), R2(R2 _, R1 _)) -> 0; + case Root(R2 _, R2(R2 _, R1 _), R2(R2 _, R2 _)) -> 0; + case Root(R2 _, R2(R2 _, R2 _), R2(R1 _, R1 _)) -> 0; + case Root(R2 _, R2(R2 _, R2 _), R2(R1 _, R2 _)) -> 0; + case Root(R2 _, R2(R2 _, R2 _), R2(R2 _, R1 _)) -> 0; +// case Root(R2 _, R2(R2 _, R2 _), R2(R2 _, R2 _)) -> 0; + }; + } + sealed interface Base {} + record R1() implements Base {} + record R2(Base b1, Base b2) implements Base {} + record Root(Base b1, Base b2, Base b3) {} + } + """, + "test.Test.Root(test.Test.R2 _,test.Test.R2(test.Test.Base _,test.Test.R2 _),test.Test.R2(test.Test.R2 _,test.Test.Base _))"); + //ideally,the result would be as follow,but it is difficult to split Base on two distinct places: +// "test.Test.Root(test.Test.R2 _,test.Test.R2(test.Test.R1 _,test.Test.R2 _),test.Test.R2(test.Test.R2 _,test.Test.R1 _))", +// "test.Test.Root(test.Test.R2 _,test.Test.R2(test.Test.R2 _,test.Test.R2 _),test.Test.R2(test.Test.R2 _,test.Test.R2 _))"); + } + + @Test + public void testComplex5(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test(Triple p) { + return switch (p) { + case Triple(B _, _, _) -> 0; + case Triple(_, A _, _) -> 0; + case Triple(_, _, A _) -> 0; +// case Triple(A _, C(Nested _, NestedBaseA _), _) -> 0; + case Triple(A _, C(Nested _, NestedBaseB _), C(Nested _, NestedBaseA _)) -> 0; + case Triple(A _, C(Nested _, NestedBaseB _), C(Nested _, NestedBaseB _)) -> 0; + case Triple(A _, C(Nested _, NestedBaseB _), C(Nested _, NestedBaseC _)) -> 0; + case Triple(A _, C(Nested _, NestedBaseC _), C(Nested _, NestedBaseA _)) -> 0; + case Triple(A _, C(Nested _, NestedBaseC _), C(Nested _, NestedBaseB _)) -> 0; +// case Path(A _, C(Nested _, NestedBaseC _), C(Nested _, NestedBaseC _)) -> 0; + }; + } + record Triple(Base c1, Base c2, Base c3) {} + sealed interface Base permits A, B {} + record A(boolean key) implements Base { + } + sealed interface B extends Base {} + record C(Nested n, NestedBase b) implements B {} + record Nested() {} + sealed interface NestedBase {} + record NestedBaseA() implements NestedBase {} + record NestedBaseB() implements NestedBase {} + record NestedBaseC() implements NestedBase {} + } + """, + "test.Test.Triple(test.Test.A _,test.Test.C(test.Test.Nested _,test.Test.NestedBaseA _),test.Test.C _)", + //the following could be: + //test.Test.Triple(test.Test.A _,test.Test.C(test.Test.Nested _,test.Test.NestedBaseC _),test.Test.C(test.Test.Nested _,test.Test.NestedBaseC _)) + "test.Test.Triple(test.Test.A _,test.Test.C(test.Test.Nested _,test.Test.NestedBaseC _),test.Test.C _)"); + } + + @Test + public void testNoInfiniteRecursion(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test(R r) { + return switch (r) { + case R(_, _, R(_, _, _, _), String s) -> 0; + case R(_, _, R(_, _, _, String str), _) -> 0; + }; + } + } + public record R(R r1, R r2, R r3, Object o) {} + """, + "test.R(test.R _,test.R _,test.R(test.R _,test.R _,test.R _,java.lang.Object _),java.lang.Object _)"); + } + + @Test + public void testEnum(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test(I i) { + return switch (i) { + case E.A -> 0; + case C _ -> 1; + }; + } + sealed interface I {} + enum E implements I {A, B} + final class C implements I {} + } + public record R(R r1, R r2, R r3, Object o) {} + """, + "test.Test.E.B"); + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test(I i) { + return switch (i) { + case C _ -> 1; + }; + } + sealed interface I {} + enum E implements I {A, B} + final class C implements I {} + } + public record R(R r1, R r2, R r3, Object o) {} + """, + "test.Test.E _"); + } + + @Test + public void testInstantiateComponentTypes(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + public class Test { + private int test(Pair> p) { + return switch (p) { + case Pair(A(A(_)) -> 0; + case Pair(A(B(_)) -> 0; + case Pair(B(A(_)) -> 0; + }; + } + record Pair(T c) {} + sealed interface Base permits A, B {} + record A(T c) implements Base {} + record B(T c) implements Base {} + } + """, + "test.Test.Pair(test.Test.B(test.Test.B _))"); + } + + @Test + public void testNeedToExpandIfRecordExists(Path base) throws Exception { + doTest(base, + new String[0], + """ + package test; + class Test { + sealed interface A { } + record B() implements A { } + record C(A a) implements A { } + + void test(A a) { + switch (a) { + case C(B _) -> throw null; + } + } + } """, + "test.Test.B _", + "test.Test.C(test.Test.C _)"); + } + + @Test + public void testComplex6(Path base) throws Exception { + doTest(base, + new String[0], + """ + public class Test { + sealed interface Base {} + record NoOp() implements Base {} + record Const() implements Base {} + record Pair(Base n1, + Base b2) implements Base {} + + int t(Base b) { + return switch (b) { + case NoOp _ -> 0; + case Const _ -> 0; + case Pair(NoOp _, _) -> 0; + case Pair(Const _, _) -> 0; + case Pair(Pair _, NoOp _) -> 0; + case Pair(Pair _, Const _) -> 0; + case Pair(Pair _, Pair(NoOp _, _)) -> 0; + case Pair(Pair _, Pair(Const _, _)) -> 0; + case Pair(Pair _, Pair(Pair(NoOp _, _), _)) -> 0; + case Pair(Pair _, Pair(Pair(Const _, _), _)) -> 0; + case Pair(Pair(NoOp _, _), Pair(Pair(Pair _, _), _)) -> 0; + case Pair(Pair(Const _, _), Pair(Pair(Pair _, _), _)) -> 0; +// case Pair(Pair(Pair _, _), Pair(Pair(Pair _, _), _)) -> 0; + }; + } + } + """, + "Test.Pair(Test.Pair(Test.Pair _,Test.Base _),Test.Pair(Test.Pair(Test.Pair _,Test.Base _),Test.Base _))"); + } + + private void doTest(Path base, String[] libraryCode, String testCode, String... expectedMissingPatterns) throws IOException { + Path current = base.resolve("."); + Path libClasses = current.resolve("libClasses"); + + Files.createDirectories(libClasses); + + if (libraryCode.length != 0) { + Path libSrc = current.resolve("lib-src"); + + for (String code : libraryCode) { + tb.writeJavaFiles(libSrc, code); + } + + new JavacTask(tb) + .outdir(libClasses) + .files(tb.findJavaFiles(libSrc)) + .run(); + } + + Path src = current.resolve("src"); + tb.writeJavaFiles(src, testCode); + + Path classes = current.resolve("libClasses"); + + Files.createDirectories(libClasses); + Set missingPatterns = new HashSet<>(); + + new JavacTask(tb) + .options("-XDrawDiagnostics", + "--class-path", libClasses.toString(), + "-XDshould-stop.at=FLOW", + "-XDshould-stop.ifNoError=FLOW", + "-XDexhaustivityMaxBaseChecks=" + Long.MAX_VALUE) //never give up + .outdir(classes) + .files(tb.findJavaFiles(src)) + .diagnosticListener(d -> { + if ("compiler.err.not.exhaustive.details".equals(d.getCode()) || + "compiler.err.not.exhaustive.statement.details".equals(d.getCode())) { + if (d instanceof DiagnosticSourceUnwrapper uw) { + d = uw.d; + } + if (d instanceof JCDiagnostic.MultilineDiagnostic diag) { + diag.getSubdiagnostics() + .stream() + .map(fragment -> fragment.toString()) + .forEach(missingPatterns::add); + } + } + }) + .run(Task.Expect.FAIL) + .writeAll(); + + Set expectedPatterns = new HashSet<>(List.of(expectedMissingPatterns)); + + if (!expectedPatterns.equals(missingPatterns)) { + throw new AssertionError("Incorrect errors, expected: " + expectedPatterns + + ", actual: " + missingPatterns); + } + } + +} diff --git a/test/langtools/tools/javac/patterns/PrimitiveInstanceOfComboTest.java b/test/langtools/tools/javac/patterns/PrimitiveInstanceOfComboTest.java index 82064bd4baf..1830afeb187 100644 --- a/test/langtools/tools/javac/patterns/PrimitiveInstanceOfComboTest.java +++ b/test/langtools/tools/javac/patterns/PrimitiveInstanceOfComboTest.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 @@ -103,16 +103,19 @@ public class PrimitiveInstanceOfComboTest extends ComboInstance { diff --git a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.java b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.java index 2890b315e62..249cdeb2464 100644 --- a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.java +++ b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchConstants.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @summary Retain exhaustiveness properties of switches with a constant selector * @enablePreview - * @compile/fail/ref=PrimitivePatternsSwitchConstants.out -XDrawDiagnostics -XDshould-stop.at=FLOW PrimitivePatternsSwitchConstants.java + * @compile/fail/ref=PrimitivePatternsSwitchConstants.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDexhaustivityMaxBaseChecks=0 PrimitivePatternsSwitchConstants.java */ public class PrimitivePatternsSwitchConstants { void testConstExpressions() { diff --git a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.java b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.java index dacd48f441c..3eab2fc83d0 100644 --- a/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.java +++ b/test/langtools/tools/javac/patterns/PrimitivePatternsSwitchErrors.java @@ -3,7 +3,7 @@ * @bug 8304487 8325653 8332463 * @summary Compiler Implementation for Primitive types in patterns, instanceof, and switch (Preview) * @enablePreview - * @compile/fail/ref=PrimitivePatternsSwitchErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW PrimitivePatternsSwitchErrors.java + * @compile/fail/ref=PrimitivePatternsSwitchErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDexhaustivityMaxBaseChecks=0 PrimitivePatternsSwitchErrors.java */ public class PrimitivePatternsSwitchErrors { record R_int(int x) {} diff --git a/test/langtools/tools/javac/patterns/SwitchErrors.java b/test/langtools/tools/javac/patterns/SwitchErrors.java index 607052be583..ebff13e7fa5 100644 --- a/test/langtools/tools/javac/patterns/SwitchErrors.java +++ b/test/langtools/tools/javac/patterns/SwitchErrors.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8262891 8269146 8269113 8348928 * @summary Verify errors related to pattern switches. - * @compile/fail/ref=SwitchErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW SwitchErrors.java + * @compile/fail/ref=SwitchErrors.out -XDrawDiagnostics -XDshould-stop.at=FLOW -XDexhaustivityMaxBaseChecks=0 SwitchErrors.java */ public class SwitchErrors { diff --git a/test/langtools/tools/javac/platform/NonExportedPermittedTypes.java b/test/langtools/tools/javac/platform/NonExportedPermittedTypes.java index bdafef7e39a..9a378ce346a 100644 --- a/test/langtools/tools/javac/platform/NonExportedPermittedTypes.java +++ b/test/langtools/tools/javac/platform/NonExportedPermittedTypes.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. * 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,9 +26,9 @@ * @bug 8318913 * @summary Verify no error is when compiling a class whose permitted types are not exported * @modules jdk.compiler - * @compile/fail/ref=NonExportedPermittedTypes.out -XDrawDiagnostics NonExportedPermittedTypes.java - * @compile/fail/ref=NonExportedPermittedTypes.out --release 21 -XDrawDiagnostics NonExportedPermittedTypes.java - * @compile/fail/ref=NonExportedPermittedTypes.out --release ${jdk.version} -XDrawDiagnostics NonExportedPermittedTypes.java + * @compile/fail/ref=NonExportedPermittedTypes.out -XDrawDiagnostics -XDexhaustivityMaxBaseChecks=0 NonExportedPermittedTypes.java + * @compile/fail/ref=NonExportedPermittedTypes.out --release 21 -XDrawDiagnostics -XDexhaustivityMaxBaseChecks=0 NonExportedPermittedTypes.java + * @compile/fail/ref=NonExportedPermittedTypes.out --release ${jdk.version} -XDrawDiagnostics -XDexhaustivityMaxBaseChecks=0 NonExportedPermittedTypes.java */ diff --git a/test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.java b/test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.java index 802e66570ef..05a73c0ba7a 100644 --- a/test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.java +++ b/test/langtools/tools/javac/switchexpr/ExpressionSwitchNotExhaustive.java @@ -2,7 +2,7 @@ * @test /nodynamiccopyright/ * @bug 8206986 * @summary Verify behavior of not exhaustive switch expressions. - * @compile/fail/ref=ExpressionSwitchNotExhaustive.out -XDrawDiagnostics ExpressionSwitchNotExhaustive.java + * @compile/fail/ref=ExpressionSwitchNotExhaustive.out -XDrawDiagnostics -XDexhaustivityMaxBaseChecks=0 ExpressionSwitchNotExhaustive.java */ public class ExpressionSwitchNotExhaustive { From a181dd09bd7ba6b23bf34327aa2be61bb00768dd Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 4 Feb 2026 11:54:23 +0000 Subject: [PATCH 123/215] 8376761: ARM32: Constant base assert after JDK-8373266 Reviewed-by: stefank, ayang, tschatzl --- src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp index d82b9d90417..2b96e978980 100644 --- a/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp +++ b/src/hotspot/os_cpu/linux_arm/javaThread_linux_arm.cpp @@ -42,6 +42,16 @@ frame JavaThread::pd_last_frame() { void JavaThread::cache_global_variables() { BarrierSet* bs = BarrierSet::barrier_set(); +#if INCLUDE_G1GC + if (bs->is_a(BarrierSet::G1BarrierSet)) { + _card_table_base = nullptr; + } else +#endif +#if INCLUDE_SHENANDOAHGC + if (bs->is_a(BarrierSet::ShenandoahBarrierSet)) { + _card_table_base = nullptr; + } else +#endif if (bs->is_a(BarrierSet::CardTableBarrierSet)) { CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set(); _card_table_base = (address)ctbs->card_table_base_const(); From 8ad91ac1109e76ee8485bf221adeac7e1751ef17 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Wed, 4 Feb 2026 12:58:38 +0000 Subject: [PATCH 124/215] 8377141: G1: Remove unused local declaration in G1BarrierSetC2 Reviewed-by: tschatzl, shade --- src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp index 61402301eb1..34d31702e80 100644 --- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp @@ -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 @@ -351,7 +351,6 @@ Node* G1BarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) co Node* G1BarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val, Node* new_val, const Type* value_type) const { - GraphKit* kit = access.kit(); if (!access.is_oop()) { return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); } @@ -361,7 +360,6 @@ Node* G1BarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access Node* G1BarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& access, Node* expected_val, Node* new_val, const Type* value_type) const { - GraphKit* kit = access.kit(); if (!access.is_oop()) { return BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type); } @@ -370,7 +368,6 @@ Node* G1BarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& acces } Node* G1BarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* value_type) const { - GraphKit* kit = access.kit(); if (!access.is_oop()) { return BarrierSetC2::atomic_xchg_at_resolved(access, new_val, value_type); } From 2a7329e2ed3a42a653f44dd061db892d104436c0 Mon Sep 17 00:00:00 2001 From: Volkan Yazici Date: Wed, 4 Feb 2026 15:05:28 +0000 Subject: [PATCH 125/215] 8376645: Test java/net/httpclient/http3/H3IdleExceedsQuicIdleTimeout.java failed: no response from peer Reviewed-by: jpai, dfuchs --- .../net/httpclient/http3/H3IdleExceedsQuicIdleTimeout.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/jdk/java/net/httpclient/http3/H3IdleExceedsQuicIdleTimeout.java b/test/jdk/java/net/httpclient/http3/H3IdleExceedsQuicIdleTimeout.java index 1028eefe828..8f39ea7b850 100644 --- a/test/jdk/java/net/httpclient/http3/H3IdleExceedsQuicIdleTimeout.java +++ b/test/jdk/java/net/httpclient/http3/H3IdleExceedsQuicIdleTimeout.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 @@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; +import jdk.httpclient.test.lib.common.HttpServerAdapters; import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestExchange; import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler; import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; @@ -63,6 +64,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; * systems * @run junit/othervm/timeout=180 -Djdk.httpclient.quic.idleTimeout=30 * -Djdk.httpclient.keepalive.timeout.h3=120 + * -Djdk.httpclient.HttpClient.log=quic:hs * ${test.main.class} */ class H3IdleExceedsQuicIdleTimeout { @@ -106,7 +108,7 @@ class H3IdleExceedsQuicIdleTimeout { assertEquals(120, Integer.parseInt(System.getProperty("jdk.httpclient.keepalive.timeout.h3")), "unexpected HTTP/3 idle timeout"); - try (final HttpClient client = HttpClient.newBuilder() + try (final HttpClient client = HttpServerAdapters.createClientBuilderForH3() .sslContext(sslCtx) .proxy(NO_PROXY) .version(HTTP_3) From d49e29aa8c88a0f966446de4288f32a529f0dd52 Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Wed, 4 Feb 2026 15:20:27 +0000 Subject: [PATCH 126/215] 8376405: Virtual thread crash: assert(!_current->is_suspended()) failed: must be Reviewed-by: sspitsyn, dholmes --- src/hotspot/share/runtime/continuation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/continuation.cpp b/src/hotspot/share/runtime/continuation.cpp index 935f304a751..0b7e64a3ba6 100644 --- a/src/hotspot/share/runtime/continuation.cpp +++ b/src/hotspot/share/runtime/continuation.cpp @@ -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 @@ -86,7 +86,8 @@ class UnmountBeginMark : public StackObj { } } ~UnmountBeginMark() { - assert(!_current->is_suspended(), "must be"); + assert(!_current->is_suspended() + JVMTI_ONLY(|| (_current->is_vthread_transition_disabler() && _result != freeze_ok)), "must be"); assert(_current->is_in_vthread_transition(), "must be"); if (_result != freeze_ok) { From 792291937f7403c9acf6c5eacf284c26c2a2857b Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Wed, 4 Feb 2026 16:55:14 +0000 Subject: [PATCH 127/215] 8340830: Console.readLine() and Console.printf() are mutually blocking Reviewed-by: jlu, jpai, rriggs, vyazici --- .../classes/java/io/ProxyingConsole.java | 106 ++++-------- .../jdk/internal/io/JdkConsoleImpl.java | 154 ++++++++---------- .../io/Console/ReadWriteBlockingTest.java | 86 ++++++++++ .../jdk/java/io/Console/readWriteBlocking.exp | 41 +++++ 4 files changed, 229 insertions(+), 158 deletions(-) create mode 100644 test/jdk/java/io/Console/ReadWriteBlockingTest.java create mode 100644 test/jdk/java/io/Console/readWriteBlocking.exp diff --git a/src/java.base/share/classes/java/io/ProxyingConsole.java b/src/java.base/share/classes/java/io/ProxyingConsole.java index 5c8da9f51da..10188f3500a 100644 --- a/src/java.base/share/classes/java/io/ProxyingConsole.java +++ b/src/java.base/share/classes/java/io/ProxyingConsole.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 @@ -27,19 +27,32 @@ package java.io; import java.nio.charset.Charset; import java.util.Locale; +import java.util.function.Supplier; +import jdk.internal.ValueBased; import jdk.internal.io.JdkConsole; /** * Console implementation for internal use. Custom Console delegate may be * provided with jdk.internal.io.JdkConsoleProvider. */ +@ValueBased final class ProxyingConsole extends Console { private final JdkConsole delegate; - private final Object readLock = new Object(); - private final Object writeLock = new Object(); - private volatile Reader reader; - private volatile PrintWriter printWriter; + private final LazyConstant reader = + LazyConstant.of(new Supplier<>(){ + @Override + public Reader get() { + return new WrappingReader(delegate.reader()); + } + }); + private final LazyConstant printWriter = + LazyConstant.of(new Supplier<>() { + @Override + public PrintWriter get() { + return new WrappingWriter(delegate.writer()); + } + }); ProxyingConsole(JdkConsole delegate) { this.delegate = delegate; @@ -50,17 +63,7 @@ final class ProxyingConsole extends Console { */ @Override public PrintWriter writer() { - PrintWriter printWriter = this.printWriter; - if (printWriter == null) { - synchronized (this) { - printWriter = this.printWriter; - if (printWriter == null) { - printWriter = new WrappingWriter(delegate.writer(), writeLock); - this.printWriter = printWriter; - } - } - } - return printWriter; + return printWriter.get(); } /** @@ -68,17 +71,7 @@ final class ProxyingConsole extends Console { */ @Override public Reader reader() { - Reader reader = this.reader; - if (reader == null) { - synchronized (this) { - reader = this.reader; - if (reader == null) { - reader = new WrappingReader(delegate.reader(), readLock); - this.reader = reader; - } - } - } - return reader; + return reader.get(); } /** @@ -94,9 +87,7 @@ final class ProxyingConsole extends Console { */ @Override public Console format(Locale locale, String format, Object ... args) { - synchronized (writeLock) { - delegate.format(locale, format, args); - } + delegate.format(locale, format, args); return this; } @@ -113,9 +104,7 @@ final class ProxyingConsole extends Console { */ @Override public Console printf(Locale locale, String format, Object ... args) { - synchronized (writeLock) { - delegate.format(locale, format, args); - } + delegate.format(locale, format, args); return this; } @@ -132,11 +121,7 @@ final class ProxyingConsole extends Console { */ @Override public String readLine(Locale locale, String format, Object ... args) { - synchronized (writeLock) { - synchronized (readLock) { - return delegate.readLine(locale, format, args); - } - } + return delegate.readLine(locale, format, args); } /** @@ -144,9 +129,7 @@ final class ProxyingConsole extends Console { */ @Override public String readLine() { - synchronized (readLock) { - return delegate.readLine(); - } + return delegate.readLine(); } /** @@ -162,11 +145,7 @@ final class ProxyingConsole extends Console { */ @Override public char[] readPassword(Locale locale, String format, Object ... args) { - synchronized (writeLock) { - synchronized (readLock) { - return delegate.readPassword(locale, format, args); - } - } + return delegate.readPassword(locale, format, args); } /** @@ -174,9 +153,7 @@ final class ProxyingConsole extends Console { */ @Override public char[] readPassword() { - synchronized (readLock) { - return delegate.readPassword(); - } + return delegate.readPassword(); } /** @@ -197,19 +174,15 @@ final class ProxyingConsole extends Console { private static final class WrappingReader extends Reader { private final Reader r; - private final Object lock; - WrappingReader(Reader r, Object lock) { - super(lock); + WrappingReader(Reader r) { + super(r); this.r = r; - this.lock = lock; } @Override public int read(char[] cbuf, int off, int len) throws IOException { - synchronized (lock) { - return r.read(cbuf, off, len); - } + return r.read(cbuf, off, len); } @Override @@ -219,25 +192,8 @@ final class ProxyingConsole extends Console { } private static final class WrappingWriter extends PrintWriter { - private final PrintWriter pw; - private final Object lock; - - public WrappingWriter(PrintWriter pw, Object lock) { - super(pw, lock); - this.pw = pw; - this.lock = lock; - } - - @Override - public void write(char[] cbuf, int off, int len) { - synchronized (lock) { - pw.write(cbuf, off, len); - } - } - - @Override - public void flush() { - pw.flush(); + public WrappingWriter(PrintWriter pw) { + super(pw); } @Override diff --git a/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java b/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java index a6f4174324e..06aafaabe88 100644 --- a/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java +++ b/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.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 @@ -32,7 +32,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.PrintWriter; import java.io.Reader; -import java.io.Writer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Formatter; @@ -84,18 +83,14 @@ public final class JdkConsoleImpl implements JdkConsole { @Override public String readLine(Locale locale, String format, Object ... args) { String line = null; - synchronized (writeLock) { - synchronized(readLock) { - if (!format.isEmpty()) - pw.format(locale, format, args); - try { - char[] ca = readline(false); - if (ca != null) - line = new String(ca); - } catch (IOException x) { - throw new IOError(x); - } - } + if (!format.isEmpty()) + pw.format(locale, format, args); + try { + char[] ca = readline(false); + if (ca != null) + line = new String(ca); + } catch (IOException x) { + throw new IOError(x); } return line; } @@ -116,24 +111,24 @@ public final class JdkConsoleImpl implements JdkConsole { // it should call this method to obtain a JdkConsoleImpl. This ensures only one Console // instance exists in the Java runtime. private static final LazyConstant> PASSWORD_CONSOLE = LazyConstant.of( - new Supplier>() { - @Override - public Optional get() { - if (System.console() != null) { - throw new IllegalStateException("Can’t create a dedicated password " + - "console since a real console already exists"); - } - - // If stdin is NOT redirected, return an Optional containing a JdkConsoleImpl - // instance, otherwise an empty Optional. - return SharedSecrets.getJavaIOAccess().isStdinTty() ? - Optional.of( - new JdkConsoleImpl( - Charset.forName(StaticProperty.stdinEncoding(), UTF_8.INSTANCE), - Charset.forName(StaticProperty.stdoutEncoding(), UTF_8.INSTANCE))) : - Optional.empty(); + new Supplier>() { + @Override + public Optional get() { + if (System.console() != null) { + throw new IllegalStateException("Can’t create a dedicated password " + + "console since a real console already exists"); } + + // If stdin is NOT redirected, return an Optional containing a JdkConsoleImpl + // instance, otherwise an empty Optional. + return SharedSecrets.getJavaIOAccess().isStdinTty() ? + Optional.of( + new JdkConsoleImpl( + Charset.forName(StaticProperty.stdinEncoding(), UTF_8.INSTANCE), + Charset.forName(StaticProperty.stdoutEncoding(), UTF_8.INSTANCE))) : + Optional.empty(); } + } ); public static Optional passwordConsole() { @@ -149,54 +144,51 @@ public final class JdkConsoleImpl implements JdkConsole { private char[] readPassword0(boolean noNewLine, Locale locale, String format, Object ... args) { char[] passwd = null; - synchronized (writeLock) { - synchronized(readLock) { - installShutdownHook(); - try { - synchronized(restoreEchoLock) { - restoreEcho = echo(false); - } - } catch (IOException x) { - throw new IOError(x); - } - IOError ioe = null; - try { - if (!format.isEmpty()) - pw.format(locale, format, args); - passwd = readline(true); - } catch (IOException x) { - ioe = new IOError(x); - } finally { - try { - synchronized(restoreEchoLock) { - if (restoreEcho) { - restoreEcho = echo(true); - } - } - } catch (IOException x) { - if (ioe == null) - ioe = new IOError(x); - else - ioe.addSuppressed(x); - } - if (ioe != null) { - if (passwd != null) { - Arrays.fill(passwd, ' '); - } - try { - if (reader instanceof LineReader lr) { - lr.zeroOut(); - } - } catch (IOException _) { - // ignore - } - throw ioe; - } - } - if (!noNewLine) { - pw.println(); - } + + installShutdownHook(); + try { + synchronized(restoreEchoLock) { + restoreEcho = echo(false); } + } catch (IOException x) { + throw new IOError(x); + } + IOError ioe = null; + try { + if (!format.isEmpty()) + pw.format(locale, format, args); + passwd = readline(true); + } catch (IOException x) { + ioe = new IOError(x); + } finally { + try { + synchronized(restoreEchoLock) { + if (restoreEcho) { + restoreEcho = echo(true); + } + } + } catch (IOException x) { + if (ioe == null) + ioe = new IOError(x); + else + ioe.addSuppressed(x); + } + if (ioe != null) { + if (passwd != null) { + Arrays.fill(passwd, ' '); + } + try { + if (reader instanceof LineReader lr) { + lr.zeroOut(); + } + } catch (IOException _) { + // ignore + } + throw ioe; + } + } + if (!noNewLine) { + pw.println(); } return passwd; } @@ -243,14 +235,11 @@ public final class JdkConsoleImpl implements JdkConsole { return outCharset; } - private final Charset inCharset; private final Charset outCharset; private final Object readLock; - private final Object writeLock; // Must not block while holding this. It is used in the shutdown hook. private final Object restoreEchoLock; private final Reader reader; - private final Writer out; private final PrintWriter pw; private final Formatter formatter; private char[] rcb; @@ -417,12 +406,11 @@ public final class JdkConsoleImpl implements JdkConsole { public JdkConsoleImpl(Charset inCharset, Charset outCharset) { Objects.requireNonNull(inCharset); Objects.requireNonNull(outCharset); - this.inCharset = inCharset; this.outCharset = outCharset; readLock = new Object(); - writeLock = new Object(); + var writeLock = new Object(); restoreEchoLock = new Object(); - out = StreamEncoder.forOutputStreamWriter( + var out = StreamEncoder.forOutputStreamWriter( new FileOutputStream(FileDescriptor.out), writeLock, outCharset); diff --git a/test/jdk/java/io/Console/ReadWriteBlockingTest.java b/test/jdk/java/io/Console/ReadWriteBlockingTest.java new file mode 100644 index 00000000000..7c05d5d257b --- /dev/null +++ b/test/jdk/java/io/Console/ReadWriteBlockingTest.java @@ -0,0 +1,86 @@ +/* + * 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 8340830 + * @summary Check if writing to Console is not blocked by other thread's read. + * This test relies on sleep(1000) to ensure that readLine() is + * invoked before printf(), which may not always occur. + * @library /test/lib + * @requires (os.family == "linux" | os.family == "mac") + * @run junit ReadWriteBlockingTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class ReadWriteBlockingTest { + + @ParameterizedTest + @ValueSource(strings = {"java.base", "jdk.internal.le"}) + @EnabledOnOs({OS.LINUX, OS.MAC}) + public void testReadWriteBlocking(String consoleModule) throws Exception { + // check "expect" command availability + var expect = Path.of("/usr/bin/expect"); + if (!Files.exists(expect) || !Files.isExecutable(expect)) { + Assumptions.abort("'" + expect + "' not found"); + } + + // invoking "expect" command + var testSrc = System.getProperty("test.src", "."); + var testClasses = System.getProperty("test.classes", "."); + var jdkDir = System.getProperty("test.jdk"); + OutputAnalyzer output = ProcessTools.executeProcess( + "/usr/bin/expect", + "-n", + testSrc + "/readWriteBlocking.exp", + jdkDir + "/bin/java", + "-classpath", testClasses, + "-Djdk.console=" + consoleModule, + "ReadWriteBlockingTest"); + output.reportDiagnosticSummary(); + output.shouldHaveExitValue(0); + } + + public static void main(String... args) { + var con = System.console(); + Thread.ofVirtual().start(() -> { + try { + // give some time for main thread to invoke readLine() + Thread.sleep(1000); + } catch (InterruptedException _) {} + con.printf("printf() invoked"); + }); + con.readLine(""); + } +} diff --git a/test/jdk/java/io/Console/readWriteBlocking.exp b/test/jdk/java/io/Console/readWriteBlocking.exp new file mode 100644 index 00000000000..202dd42db3f --- /dev/null +++ b/test/jdk/java/io/Console/readWriteBlocking.exp @@ -0,0 +1,41 @@ +# +# 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. +# + +set timeout 10 +eval spawn $argv + +expect { + "printf() invoked" { + send -- "\n" + } + timeout { + puts stderr "ERROR: Timed out waiting for the expected text" + exit 1 + } + eof { + puts stderr "ERROR: Process exited before printing the expected text" + exit 1 + } +} + +expect eof From 949370ab0e701cfcc68cb84dd0f91e5db41f4f45 Mon Sep 17 00:00:00 2001 From: William Kemper Date: Wed, 4 Feb 2026 19:33:10 +0000 Subject: [PATCH 128/215] 8376756: GenShen: Improve encapsulation of generational collection set choosing Reviewed-by: shade, kdnilsen --- .../shenandoahAdaptiveHeuristics.cpp | 7 +- .../shenandoahAdaptiveHeuristics.hpp | 24 +- .../shenandoahAggressiveHeuristics.cpp | 7 +- .../shenandoahAggressiveHeuristics.hpp | 16 +- .../shenandoahCompactHeuristics.cpp | 7 +- .../shenandoahCompactHeuristics.hpp | 16 +- .../shenandoahGenerationalHeuristics.cpp | 547 +++++++++++++++++- .../shenandoahGenerationalHeuristics.hpp | 37 +- .../heuristics/shenandoahGlobalHeuristics.cpp | 9 +- .../heuristics/shenandoahGlobalHeuristics.hpp | 6 +- .../heuristics/shenandoahHeuristics.cpp | 3 +- .../heuristics/shenandoahHeuristics.hpp | 12 +- .../heuristics/shenandoahOldHeuristics.cpp | 7 +- .../heuristics/shenandoahOldHeuristics.hpp | 4 +- .../shenandoahPassiveHeuristics.cpp | 7 +- .../shenandoahPassiveHeuristics.hpp | 18 +- .../heuristics/shenandoahStaticHeuristics.cpp | 9 +- .../heuristics/shenandoahStaticHeuristics.hpp | 18 +- .../heuristics/shenandoahYoungHeuristics.cpp | 8 +- .../heuristics/shenandoahYoungHeuristics.hpp | 6 +- .../gc/shenandoah/shenandoahGeneration.cpp | 530 +---------------- .../gc/shenandoah/shenandoahGeneration.hpp | 25 - 22 files changed, 662 insertions(+), 661 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp index 46d9f19d35f..7a8bd55c795 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp @@ -68,9 +68,9 @@ ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics(ShenandoahSpaceInfo* ShenandoahAdaptiveHeuristics::~ShenandoahAdaptiveHeuristics() {} -size_t ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { size_t garbage_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100; // The logic for cset selection in adaptive is as follows: @@ -124,7 +124,6 @@ size_t ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shena cur_garbage = new_garbage; } } - return 0; } void ShenandoahAdaptiveHeuristics::record_cycle_start() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp index c4fdf819391..9b7824a50d7 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp @@ -33,7 +33,7 @@ #include "utilities/numberSeq.hpp" /** - * ShenanoahAllocationRate maintains a truncated history of recently sampled allocation rates for the purpose of providing + * ShenandoahAllocationRate maintains a truncated history of recently sampled allocation rates for the purpose of providing * informed estimates of current and future allocation rates based on weighted averages and standard deviations of the * truncated history. More recently sampled allocations are weighted more heavily than older samples when computing * averages and standard deviations. @@ -108,20 +108,20 @@ public: virtual ~ShenandoahAdaptiveHeuristics(); - virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, RegionData* data, size_t size, size_t actual_free) override; - virtual void record_cycle_start() override; - virtual void record_success_concurrent() override; - virtual void record_degenerated() override; - virtual void record_success_full() override; + void record_cycle_start() override; + void record_success_concurrent() override; + void record_degenerated() override; + void record_success_full() override; - virtual bool should_start_gc() override; + bool should_start_gc() override; - virtual const char* name() override { return "Adaptive"; } - virtual bool is_diagnostic() override { return false; } - virtual bool is_experimental() override { return false; } + const char* name() override { return "Adaptive"; } + bool is_diagnostic() override { return false; } + bool is_experimental() override { return false; } private: // These are used to adjust the margin of error and the spike threshold @@ -185,7 +185,7 @@ protected: // in the generational case. Controlled by global flag ShenandoahMinFreeThreshold. size_t min_free_threshold(); - inline void accept_trigger_with_type(Trigger trigger_type) { + void accept_trigger_with_type(Trigger trigger_type) { _last_trigger = trigger_type; ShenandoahHeuristics::accept_trigger(); } @@ -193,7 +193,7 @@ protected: public: // Sample the allocation rate at GC trigger time if possible. Return the number of allocated bytes that were // not accounted for in the sample. This must be called before resetting bytes allocated since gc start. - virtual size_t force_alloc_rate_sample(size_t bytes_allocated) override { + size_t force_alloc_rate_sample(size_t bytes_allocated) override { size_t unaccounted_bytes; _allocation_rate.force_sample(bytes_allocated, unaccounted_bytes); return unaccounted_bytes; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp index 990b59ec853..a833e39631c 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.cpp @@ -39,16 +39,15 @@ ShenandoahAggressiveHeuristics::ShenandoahAggressiveHeuristics(ShenandoahSpaceIn SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahEvacReserveOverflow); } -size_t ShenandoahAggressiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t free) { +void ShenandoahAggressiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t free) { for (size_t idx = 0; idx < size; idx++) { ShenandoahHeapRegion* r = data[idx].get_region(); if (r->garbage() > 0) { cset->add_region(r); } } - return 0; } bool ShenandoahAggressiveHeuristics::should_start_gc() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp index 25c8635489f..9dc88a61bf5 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp @@ -35,17 +35,17 @@ class ShenandoahAggressiveHeuristics : public ShenandoahHeuristics { public: ShenandoahAggressiveHeuristics(ShenandoahSpaceInfo* space_info); - virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t free); + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t free) override; - virtual bool should_start_gc(); + bool should_start_gc() override; - virtual bool should_unload_classes(); + bool should_unload_classes() override; - virtual const char* name() { return "Aggressive"; } - virtual bool is_diagnostic() { return true; } - virtual bool is_experimental() { return false; } + const char* name() override { return "Aggressive"; } + bool is_diagnostic() override { return true; } + bool is_experimental() override { return false; } }; #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHAGGRESSIVEHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp index 09a8394a4b1..28673b28612 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp @@ -76,9 +76,9 @@ bool ShenandoahCompactHeuristics::should_start_gc() { return ShenandoahHeuristics::should_start_gc(); } -size_t ShenandoahCompactHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +void ShenandoahCompactHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { // Do not select too large CSet that would overflow the available free space size_t max_cset = actual_free * 3 / 4; @@ -97,5 +97,4 @@ size_t ShenandoahCompactHeuristics::choose_collection_set_from_regiondata(Shenan cset->add_region(r); } } - return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp index 4988d5d495d..a32c9c88478 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp @@ -33,17 +33,17 @@ */ class ShenandoahCompactHeuristics : public ShenandoahHeuristics { public: - ShenandoahCompactHeuristics(ShenandoahSpaceInfo* space_info); + explicit ShenandoahCompactHeuristics(ShenandoahSpaceInfo* space_info); - virtual bool should_start_gc(); + bool should_start_gc() override; - virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free); + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; - virtual const char* name() { return "Compact"; } - virtual bool is_diagnostic() { return false; } - virtual bool is_experimental() { return false; } + const char* name() override { return "Compact"; } + bool is_diagnostic() override { return false; } + bool is_experimental() override { return false; } }; #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHCOMPACTHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp index ee315ce5c7e..80e6decf57d 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp @@ -25,19 +25,205 @@ #include "gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" +#include "gc/shenandoah/shenandoahCollectionSetPreselector.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" #include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp" #include "gc/shenandoah/shenandoahOldGeneration.hpp" #include "gc/shenandoah/shenandoahTrace.hpp" +#include "gc/shenandoah/shenandoahYoungGeneration.hpp" #include "logging/log.hpp" +#include "utilities/quickSort.hpp" -ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation) - : ShenandoahAdaptiveHeuristics(generation), _generation(generation) { +using idx_t = ShenandoahSimpleBitMap::idx_t; + +typedef struct { + ShenandoahHeapRegion* _region; + size_t _live_data; +} AgedRegionData; + +static int compare_by_aged_live(AgedRegionData a, AgedRegionData b) { + if (a._live_data < b._live_data) + return -1; + if (a._live_data > b._live_data) + return 1; + return 0; } -size_t ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { +inline void assert_no_in_place_promotions() { +#ifdef ASSERT + class ShenandoahNoInPlacePromotions : public ShenandoahHeapRegionClosure { + public: + void heap_region_do(ShenandoahHeapRegion *r) override { + assert(r->get_top_before_promote() == nullptr, + "Region %zu should not be ready for in-place promotion", r->index()); + } + } cl; + ShenandoahHeap::heap()->heap_region_iterate(&cl); +#endif +} + +ShenandoahGenerationalHeuristics::ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation) + : ShenandoahAdaptiveHeuristics(generation), _generation(generation), _add_regions_to_old(0) { +} + +void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { + ShenandoahHeap* heap = ShenandoahHeap::heap(); + + _add_regions_to_old = 0; + + // Seed the collection set with resource area-allocated + // preselected regions, which are removed when we exit this scope. + ShenandoahCollectionSetPreselector preselector(collection_set, heap->num_regions()); + + // Find the amount that will be promoted, regions that will be promoted in + // place, and preselected older regions that will be promoted by evacuation. + compute_evacuation_budgets(heap); + + // Choose the collection set, including the regions preselected above for promotion into the old generation. + filter_regions(collection_set); + + // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. + adjust_evacuation_budgets(heap, collection_set); + + if (_generation->is_global()) { + // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so + // the remembered set scan can use that to avoid walking into garbage. When the next old mark begins, we will + // use the mark bitmap to make the old regions parsable by coalescing and filling any unmarked objects. Thus, + // we prepare for old collections by remembering which regions are old at this time. Note that any objects + // promoted into old regions will be above TAMS, and so will be considered marked. However, free regions that + // become old after this point will not be covered correctly by the mark bitmap, so we must be careful not to + // coalesce those regions. Only the old regions which are not part of the collection set at this point are + // eligible for coalescing. As implemented now, this has the side effect of possibly initiating mixed-evacuations + // after a global cycle for old regions that were not included in this collection set. + heap->old_generation()->prepare_for_mixed_collections_after_global_gc(); + } +} + +void ShenandoahGenerationalHeuristics::compute_evacuation_budgets(ShenandoahHeap* const heap) { + shenandoah_assert_generational(); + + ShenandoahOldGeneration* const old_generation = heap->old_generation(); + ShenandoahYoungGeneration* const young_generation = heap->young_generation(); + const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + + // During initialization and phase changes, it is more likely that fewer objects die young and old-gen + // memory is not yet full (or is in the process of being replaced). During these times especially, it + // is beneficial to loan memory from old-gen to young-gen during the evacuation and update-refs phases + // of execution. + + // Calculate EvacuationReserve before PromotionReserve. Evacuation is more critical than promotion. + // If we cannot evacuate old-gen, we will not be able to reclaim old-gen memory. Promotions are less + // critical. If we cannot promote, there may be degradation of young-gen memory because old objects + // accumulate there until they can be promoted. This increases the young-gen marking and evacuation work. + + // First priority is to reclaim the easy garbage out of young-gen. + + // maximum_young_evacuation_reserve is upper bound on memory to be evacuated into young Collector Reserve. This is + // bounded at the end of previous GC cycle, based on available memory and balancing of evacuation to old and young. + size_t maximum_young_evacuation_reserve = young_generation->get_evacuation_reserve(); + + // maximum_old_evacuation_reserve is an upper bound on memory evacuated from old and evacuated to old (promoted), + // clamped by the old generation space available. + // + // Here's the algebra. + // Let SOEP = ShenandoahOldEvacPercent, + // OE = old evac, + // YE = young evac, and + // TE = total evac = OE + YE + // By definition: + // SOEP/100 = OE/TE + // = OE/(OE+YE) + // => SOEP/(100-SOEP) = OE/((OE+YE)-OE) // componendo-dividendo: If a/b = c/d, then a/(b-a) = c/(d-c) + // = OE/YE + // => OE = YE*SOEP/(100-SOEP) + + // We have to be careful in the event that SOEP is set to 100 by the user. + assert(ShenandoahOldEvacPercent <= 100, "Error"); + const size_t old_available = old_generation->available(); + const size_t maximum_old_evacuation_reserve = (ShenandoahOldEvacPercent == 100) ? + old_available : MIN2((maximum_young_evacuation_reserve * ShenandoahOldEvacPercent) / (100 - ShenandoahOldEvacPercent), + old_available); + + // In some cases, maximum_old_reserve < old_available (when limited by ShenandoahOldEvacPercent) + // This limit affects mixed evacuations, but does not affect promotions. + + // Second priority is to reclaim garbage out of old-gen if there are old-gen collection candidates. Third priority + // is to promote as much as we have room to promote. However, if old-gen memory is in short supply, this means young + // GC is operating under "duress" and was unable to transfer the memory that we would normally expect. In this case, + // old-gen will refrain from compacting itself in order to allow a quicker young-gen cycle (by avoiding the update-refs + // through ALL of old-gen). If there is some memory available in old-gen, we will use this for promotions as promotions + // do not add to the update-refs burden of GC. + + size_t old_evacuation_reserve, old_promo_reserve; + if (_generation->is_global()) { + // Global GC is typically triggered by user invocation of System.gc(), and typically indicates that there is lots + // of garbage to be reclaimed because we are starting a new phase of execution. Marking for global GC may take + // significantly longer than typical young marking because we must mark through all old objects. To expedite + // evacuation and update-refs, we give emphasis to reclaiming garbage first, wherever that garbage is found. + // Global GC will adjust generation sizes to accommodate the collection set it chooses. + + // Use remnant of old_available to hold promotions. + old_promo_reserve = old_available - maximum_old_evacuation_reserve; + + // Dedicate all available old memory to old_evacuation reserve. This may be small, because old-gen is only + // expanded based on an existing mixed evacuation workload at the end of the previous GC cycle. We'll expand + // the budget for evacuation of old during GLOBAL cset selection. + old_evacuation_reserve = maximum_old_evacuation_reserve; + } else if (old_generation->has_unprocessed_collection_candidates()) { + // We reserved all old-gen memory at end of previous GC to hold anticipated evacuations to old-gen. If this is + // mixed evacuation, reserve all of this memory for compaction of old-gen and do not promote. Prioritize compaction + // over promotion in order to defragment OLD so that it will be better prepared to efficiently receive promoted memory. + old_evacuation_reserve = maximum_old_evacuation_reserve; + old_promo_reserve = old_available - maximum_old_evacuation_reserve; + } else { + // Make all old-evacuation memory for promotion, but if we can't use it all for promotion, we'll allow some evacuation. + old_evacuation_reserve = old_available - maximum_old_evacuation_reserve; + old_promo_reserve = maximum_old_evacuation_reserve; + } + assert(old_evacuation_reserve <= old_available, "Error"); + + + // We see too many old-evacuation failures if we force ourselves to evacuate into regions that are not initially empty. + // So we limit the old-evacuation reserve to unfragmented memory. Even so, old-evacuation is free to fill in nooks and + // crannies within existing partially used regions and it generally tries to do so. + const size_t old_free_unfragmented = old_generation->free_unaffiliated_regions() * region_size_bytes; + if (old_evacuation_reserve > old_free_unfragmented) { + const size_t delta = old_evacuation_reserve - old_free_unfragmented; + old_evacuation_reserve -= delta; + // Let promo consume fragments of old-gen memory + old_promo_reserve += delta; + } + + // If is_global(), we let garbage-first heuristic determine cset membership. Otherwise, we give priority + // to tenurable regions by preselecting regions for promotion by evacuation (obtaining the live data to seed promoted_reserve). + // This also identifies regions that will be promoted in place. These use the tenuring threshold. + const size_t consumed_by_advance_promotion = select_aged_regions(_generation->is_global()? 0: old_promo_reserve); + assert(consumed_by_advance_promotion <= old_promo_reserve, "Do not promote more than budgeted"); + + // The young evacuation reserve can be no larger than young_unaffiliated. Planning to evacuate into partially consumed + // young regions is doomed to failure if any of those partially consumed regions is selected for the collection set. + size_t young_unaffiliated = young_generation->free_unaffiliated_regions() * region_size_bytes; + + // If any regions have been selected for promotion in place, this has the effect of decreasing available within mutator + // and collector partitions, due to padding of remnant memory within each promoted in place region. This will affect + // young_evacuation_reserve but not old_evacuation_reserve or consumed_by_advance_promotion. So recompute. + size_t young_evacuation_reserve = MIN2(maximum_young_evacuation_reserve, young_unaffiliated); + + // Note that unused old_promo_reserve might not be entirely consumed_by_advance_promotion. Do not transfer this + // to old_evacuation_reserve because this memory is likely very fragmented, and we do not want to increase the likelihood + // of old evacuation failure. Leave this memory in the promoted reserve as it may be targeted by opportunistic + // promotions (found during evacuation of young regions). + young_generation->set_evacuation_reserve(young_evacuation_reserve); + old_generation->set_evacuation_reserve(old_evacuation_reserve); + old_generation->set_promoted_reserve(old_promo_reserve); + + // There is no need to expand OLD because all memory used here was set aside at end of previous GC, except in the + // case of a GLOBAL gc. During choose_collection_set() of GLOBAL, old will be expanded on demand. +} + +void ShenandoahGenerationalHeuristics::filter_regions(ShenandoahCollectionSet* collection_set) { assert(collection_set->is_empty(), "Must be empty"); auto heap = ShenandoahGenerationalHeap::heap(); @@ -170,10 +356,9 @@ size_t ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollect size_t immediate_percent = (total_garbage == 0) ? 0 : (immediate_garbage * 100 / total_garbage); bool doing_promote_in_place = (humongous_regions_promoted + regular_regions_promoted_in_place > 0); - size_t add_regions_to_old = 0; if (doing_promote_in_place || (preselected_candidates > 0) || (immediate_percent <= ShenandoahImmediateThreshold)) { // Call the subclasses to add young-gen regions into the collection set. - add_regions_to_old = choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); + choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); } if (collection_set->has_old_regions()) { @@ -190,9 +375,359 @@ size_t ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollect regular_regions_promoted_free, immediate_regions, immediate_garbage); - return add_regions_to_old; } +// Preselect for inclusion into the collection set all regions whose age is at or above tenure age and for which the +// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We +// identify these regions by setting the appropriate entry of the collection set's preselected regions array to true. +// All entries are initialized to false before calling this function. +// +// During the subsequent selection of the collection set, we give priority to these promotion set candidates. +// Without this prioritization, we found that the aged regions tend to be ignored because they typically have +// much less garbage and much more live data than the recently allocated "eden" regions. When aged regions are +// repeatedly excluded from the collection set, the amount of live memory within the young generation tends to +// accumulate and this has the undesirable side effect of causing young-generation collections to require much more +// CPU and wall-clock time. +// +// A second benefit of treating aged regions differently than other regions during collection set selection is +// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation +// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be +// reserved in the young generation. +size_t ShenandoahGenerationalHeuristics::select_aged_regions(const size_t old_promotion_reserve) { + + // There should be no regions configured for subsequent in-place-promotions carried over from the previous cycle. + assert_no_in_place_promotions(); + + auto const heap = ShenandoahGenerationalHeap::heap(); + ShenandoahFreeSet* free_set = heap->free_set(); + bool* const candidate_regions_for_promotion_by_copy = heap->collection_set()->preselected_regions(); + ShenandoahMarkingContext* const ctx = heap->marking_context(); + + const size_t old_garbage_threshold = + (ShenandoahHeapRegion::region_size_bytes() * heap->old_generation()->heuristics()->get_old_garbage_threshold()) / 100; + + const size_t pip_used_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahGenerationalMinPIPUsage) / 100; + + size_t promo_potential = 0; + size_t candidates = 0; + + // Tracks the padding of space above top in regions eligible for promotion in place + size_t promote_in_place_pad = 0; + + // Sort the promotion-eligible regions in order of increasing live-data-bytes so that we can first reclaim regions that require + // less evacuation effort. This prioritizes garbage first, expanding the allocation pool early before we reclaim regions that + // have more live data. + const idx_t num_regions = heap->num_regions(); + + ResourceMark rm; + AgedRegionData* sorted_regions = NEW_RESOURCE_ARRAY(AgedRegionData, num_regions); + + ShenandoahFreeSet* freeset = heap->free_set(); + + // Any region that is to be promoted in place needs to be retired from its Collector or Mutator partition. + idx_t pip_low_collector_idx = freeset->max_regions(); + idx_t pip_high_collector_idx = -1; + idx_t pip_low_mutator_idx = freeset->max_regions(); + idx_t pip_high_mutator_idx = -1; + size_t collector_regions_to_pip = 0; + size_t mutator_regions_to_pip = 0; + + size_t pip_mutator_regions = 0; + size_t pip_collector_regions = 0; + size_t pip_mutator_bytes = 0; + size_t pip_collector_bytes = 0; + + for (idx_t i = 0; i < num_regions; i++) { + ShenandoahHeapRegion* const r = heap->get_region(i); + if (r->is_empty() || !r->has_live() || !r->is_young() || !r->is_regular()) { + // skip over regions that aren't regular young with some live data + continue; + } + if (heap->is_tenurable(r)) { + if ((r->garbage() < old_garbage_threshold) && (r->used() > pip_used_threshold)) { + // We prefer to promote this region in place because it has a small amount of garbage and a large usage. + HeapWord* tams = ctx->top_at_mark_start(r); + HeapWord* original_top = r->top(); + if (!heap->is_concurrent_old_mark_in_progress() && tams == original_top) { + // No allocations from this region have been made during concurrent mark. It meets all the criteria + // for in-place-promotion. Though we only need the value of top when we fill the end of the region, + // we use this field to indicate that this region should be promoted in place during the evacuation + // phase. + r->save_top_before_promote(); + size_t remnant_bytes = r->free(); + size_t remnant_words = remnant_bytes / HeapWordSize; + assert(ShenandoahHeap::min_fill_size() <= PLAB::min_size(), "Implementation makes invalid assumptions"); + if (remnant_words >= ShenandoahHeap::min_fill_size()) { + ShenandoahHeap::fill_with_object(original_top, remnant_words); + // Fill the remnant memory within this region to assure no allocations prior to promote in place. Otherwise, + // newly allocated objects will not be parsable when promote in place tries to register them. Furthermore, any + // new allocations would not necessarily be eligible for promotion. This addresses both issues. + r->set_top(r->end()); + // The region r is either in the Mutator or Collector partition if remnant_words > heap()->plab_min_size. + // Otherwise, the region is in the NotFree partition. + ShenandoahFreeSetPartitionId p = free_set->membership(i); + if (p == ShenandoahFreeSetPartitionId::Mutator) { + mutator_regions_to_pip++; + if (i < pip_low_mutator_idx) { + pip_low_mutator_idx = i; + } + if (i > pip_high_mutator_idx) { + pip_high_mutator_idx = i; + } + pip_mutator_regions++; + pip_mutator_bytes += remnant_bytes; + } else if (p == ShenandoahFreeSetPartitionId::Collector) { + collector_regions_to_pip++; + if (i < pip_low_collector_idx) { + pip_low_collector_idx = i; + } + if (i > pip_high_collector_idx) { + pip_high_collector_idx = i; + } + pip_collector_regions++; + pip_collector_bytes += remnant_bytes; + } else { + assert((p == ShenandoahFreeSetPartitionId::NotFree) && (remnant_words < heap->plab_min_size()), + "Should be NotFree if not in Collector or Mutator partitions"); + // In this case, the memory is already counted as used and the region has already been retired. There is + // no need for further adjustments to used. Further, the remnant memory for this region will not be + // unallocated or made available to OldCollector after pip. + remnant_bytes = 0; + } + promote_in_place_pad += remnant_bytes; + free_set->prepare_to_promote_in_place(i, remnant_bytes); + } else { + // Since the remnant is so small that this region has already been retired, we don't have to worry about any + // accidental allocations occurring within this region before the region is promoted in place. + + // This region was already not in the Collector or Mutator set, so no need to remove it. + assert(free_set->membership(i) == ShenandoahFreeSetPartitionId::NotFree, "sanity"); + } + } + // Else, we do not promote this region (either in place or by copy) because it has received new allocations. + + // During evacuation, we exclude from promotion regions for which age > tenure threshold, garbage < garbage-threshold, + // used > pip_used_threshold, and get_top_before_promote() != tams + } else { + // Record this promotion-eligible candidate region. After sorting and selecting the best candidates below, + // we may still decide to exclude this promotion-eligible region from the current collection set. If this + // happens, we will consider this region as part of the anticipated promotion potential for the next GC + // pass; see further below. + sorted_regions[candidates]._region = r; + sorted_regions[candidates++]._live_data = r->get_live_data_bytes(); + } + } else { + // We only evacuate & promote objects from regular regions whose garbage() is above old-garbage-threshold. + // Objects in tenure-worthy regions with less garbage are promoted in place. These take a different path to + // old-gen. Regions excluded from promotion because their garbage content is too low (causing us to anticipate that + // the region would be promoted in place) may be eligible for evacuation promotion by the time promotion takes + // place during a subsequent GC pass because more garbage is found within the region between now and then. This + // should not happen if we are properly adapting the tenure age. The theory behind adaptive tenuring threshold + // is to choose the youngest age that demonstrates no "significant" further loss of population since the previous + // age. If not this, we expect the tenure age to demonstrate linear population decay for at least two population + // samples, whereas we expect to observe exponential population decay for ages younger than the tenure age. + // + // In the case that certain regions which were anticipated to be promoted in place need to be promoted by + // evacuation, it may be the case that there is not sufficient reserve within old-gen to hold evacuation of + // these regions. The likely outcome is that these regions will not be selected for evacuation or promotion + // in the current cycle and we will anticipate that they will be promoted in the next cycle. This will cause + // us to reserve more old-gen memory so that these objects can be promoted in the subsequent cycle. + if (heap->is_aging_cycle() && heap->age_census()->is_tenurable(r->age() + 1)) { + if (r->garbage() >= old_garbage_threshold) { + promo_potential += r->get_live_data_bytes(); + } + } + } + // Note that we keep going even if one region is excluded from selection. + // Subsequent regions may be selected if they have smaller live data. + } + + if (pip_mutator_regions + pip_collector_regions > 0) { + freeset->account_for_pip_regions(pip_mutator_regions, pip_mutator_bytes, pip_collector_regions, pip_collector_bytes); + } + + // Retire any regions that have been selected for promote in place + if (collector_regions_to_pip > 0) { + freeset->shrink_interval_if_range_modifies_either_boundary(ShenandoahFreeSetPartitionId::Collector, + pip_low_collector_idx, pip_high_collector_idx, + collector_regions_to_pip); + } + if (mutator_regions_to_pip > 0) { + freeset->shrink_interval_if_range_modifies_either_boundary(ShenandoahFreeSetPartitionId::Mutator, + pip_low_mutator_idx, pip_high_mutator_idx, + mutator_regions_to_pip); + } + + // Sort in increasing order according to live data bytes. Note that candidates represents the number of regions + // that qualify to be promoted by evacuation. + size_t old_consumed = 0; + if (candidates > 0) { + size_t selected_regions = 0; + size_t selected_live = 0; + QuickSort::sort(sorted_regions, candidates, compare_by_aged_live); + for (size_t i = 0; i < candidates; i++) { + ShenandoahHeapRegion* const region = sorted_regions[i]._region; + const size_t region_live_data = sorted_regions[i]._live_data; + const size_t promotion_need = (size_t) (region_live_data * ShenandoahPromoEvacWaste); + if (old_consumed + promotion_need <= old_promotion_reserve) { + old_consumed += promotion_need; + candidate_regions_for_promotion_by_copy[region->index()] = true; + selected_regions++; + selected_live += region_live_data; + } else { + // We rejected this promotable region from the collection set because we had no room to hold its copy. + // Add this region to promo potential for next GC. + promo_potential += region_live_data; + assert(!candidate_regions_for_promotion_by_copy[region->index()], "Shouldn't be selected"); + } + // We keep going even if one region is excluded from selection because we need to accumulate all eligible + // regions that are not preselected into promo_potential + } + log_debug(gc, ergo)("Preselected %zu regions containing " PROPERFMT " live data," + " consuming: " PROPERFMT " of budgeted: " PROPERFMT, + selected_regions, PROPERFMTARGS(selected_live), PROPERFMTARGS(old_consumed), PROPERFMTARGS(old_promotion_reserve)); + } + + log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential)); + + heap->old_generation()->set_pad_for_promote_in_place(promote_in_place_pad); + heap->old_generation()->set_promotion_potential(promo_potential); + return old_consumed; +} + +// Having chosen the collection set, adjust the budgets for generational mode based on its composition. Note +// that young_generation->available() now knows about recently discovered immediate garbage. +void ShenandoahGenerationalHeuristics::adjust_evacuation_budgets(ShenandoahHeap* const heap, + ShenandoahCollectionSet* const collection_set) { + shenandoah_assert_generational(); + // We may find that old_evacuation_reserve and/or loaned_for_young_evacuation are not fully consumed, in which case we may + // be able to increase regions_available_to_loan + + // The role of adjust_evacuation_budgets() is to compute the correct value of regions_available_to_loan and to make + // effective use of this memory, including the remnant memory within these regions that may result from rounding loan to + // integral number of regions. Excess memory that is available to be loaned is applied to an allocation supplement, + // which allows mutators to allocate memory beyond the current capacity of young-gen on the promise that the loan + // will be repaid as soon as we finish updating references for the recently evacuated collection set. + + // We cannot recalculate regions_available_to_loan by simply dividing old_generation->available() by region_size_bytes + // because the available memory may be distributed between many partially occupied regions that are already holding old-gen + // objects. Memory in partially occupied regions is not "available" to be loaned. Note that an increase in old-gen + // available that results from a decrease in memory consumed by old evacuation is not necessarily available to be loaned + // to young-gen. + + const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); + ShenandoahOldGeneration* const old_generation = heap->old_generation(); + ShenandoahYoungGeneration* const young_generation = heap->young_generation(); + + const size_t old_evacuated = collection_set->get_live_bytes_in_old_regions(); + size_t old_evacuated_committed = (size_t) (ShenandoahOldEvacWaste * double(old_evacuated)); + size_t old_evacuation_reserve = old_generation->get_evacuation_reserve(); + + if (old_evacuated_committed > old_evacuation_reserve) { + // This should only happen due to round-off errors when enforcing ShenandoahOldEvacWaste + assert(old_evacuated_committed <= (33 * old_evacuation_reserve) / 32, + "Round-off errors should be less than 3.125%%, committed: %zu, reserved: %zu", + old_evacuated_committed, old_evacuation_reserve); + old_evacuated_committed = old_evacuation_reserve; + // Leave old_evac_reserve as previously configured + } else if (old_evacuated_committed < old_evacuation_reserve) { + // This happens if the old-gen collection consumes less than full budget. + log_debug(gc, cset)("Shrinking old evac reserve to match old_evac_commited: " PROPERFMT, + PROPERFMTARGS(old_evacuated_committed)); + old_evacuation_reserve = old_evacuated_committed; + old_generation->set_evacuation_reserve(old_evacuation_reserve); + } + + size_t young_advance_promoted = collection_set->get_live_bytes_in_tenurable_regions(); + size_t young_advance_promoted_reserve_used = (size_t) (ShenandoahPromoEvacWaste * double(young_advance_promoted)); + + size_t young_evacuated = collection_set->get_live_bytes_in_untenurable_regions(); + size_t young_evacuated_reserve_used = (size_t) (ShenandoahEvacWaste * double(young_evacuated)); + + size_t total_young_available = young_generation->available_with_reserve() - _add_regions_to_old * region_size_bytes;; + assert(young_evacuated_reserve_used <= total_young_available, "Cannot evacuate (%zu) more than is available in young (%zu)", + young_evacuated_reserve_used, total_young_available); + young_generation->set_evacuation_reserve(young_evacuated_reserve_used); + + // We have not yet rebuilt the free set. Some of the memory that is thought to be avaiable within old may no + // longer be available if that memory had been free within regions that were selected for the collection set. + // Make the necessary adjustments to old_available. + size_t old_available = + old_generation->available() + _add_regions_to_old * region_size_bytes - collection_set->get_old_available_bytes_collected(); + + // Now that we've established the collection set, we know how much memory is really required by old-gen for evacuation + // and promotion reserves. Try shrinking OLD now in case that gives us a bit more runway for mutator allocations during + // evac and update phases. + size_t old_consumed = old_evacuated_committed + young_advance_promoted_reserve_used; + + if (old_available < old_consumed) { + // This can happen due to round-off errors when adding the results of truncated integer arithmetic. + // We've already truncated old_evacuated_committed. Truncate young_advance_promoted_reserve_used here. + + assert(young_advance_promoted_reserve_used <= (33 * (old_available - old_evacuated_committed)) / 32, + "Round-off errors should be less than 3.125%%, committed: %zu, reserved: %zu", + young_advance_promoted_reserve_used, old_available - old_evacuated_committed); + if (old_available > old_evacuated_committed) { + young_advance_promoted_reserve_used = old_available - old_evacuated_committed; + } else { + young_advance_promoted_reserve_used = 0; + old_evacuated_committed = old_available; + } + // TODO: reserve for full promotion reserve, not just for advance (preselected) promotion + old_consumed = old_evacuated_committed + young_advance_promoted_reserve_used; + } + + assert(old_available >= old_consumed, "Cannot consume (%zu) more than is available (%zu)", + old_consumed, old_available); + size_t excess_old = old_available - old_consumed; + size_t unaffiliated_old_regions = old_generation->free_unaffiliated_regions() + _add_regions_to_old; + size_t unaffiliated_old = unaffiliated_old_regions * region_size_bytes; + assert(unaffiliated_old >= old_evacuated_committed, "Do not evacuate (%zu) more than unaffiliated old (%zu)", + old_evacuated_committed, unaffiliated_old); + + // Make sure old_evac_committed is unaffiliated + if (old_evacuated_committed > 0) { + if (unaffiliated_old > old_evacuated_committed) { + size_t giveaway = unaffiliated_old - old_evacuated_committed; + size_t giveaway_regions = giveaway / region_size_bytes; // round down + if (giveaway_regions > 0) { + excess_old = MIN2(excess_old, giveaway_regions * region_size_bytes); + } else { + excess_old = 0; + } + } else { + excess_old = 0; + } + } + + // If we find that OLD has excess regions, give them back to YOUNG now to reduce likelihood we run out of allocation + // runway during evacuation and update-refs. We may make further adjustments to balance. + ssize_t add_regions_to_young = 0; + if (excess_old > unaffiliated_old) { + // we can give back unaffiliated_old (all of unaffiliated is excess) + if (unaffiliated_old_regions > 0) { + add_regions_to_young = unaffiliated_old_regions; + } + } else if (unaffiliated_old_regions > 0) { + // excess_old < unaffiliated old: we can give back MIN(excess_old/region_size_bytes, unaffiliated_old_regions) + size_t excess_regions = excess_old / region_size_bytes; + add_regions_to_young = MIN2(excess_regions, unaffiliated_old_regions); + } + + if (add_regions_to_young > 0) { + assert(excess_old >= add_regions_to_young * region_size_bytes, "Cannot xfer more than excess old"); + excess_old -= add_regions_to_young * region_size_bytes; + log_debug(gc, ergo)("Before start of evacuation, total_promotion reserve is young_advance_promoted_reserve: %zu " + "plus excess: old: %zu", young_advance_promoted_reserve_used, excess_old); + } + + // Add in the excess_old memory to hold unanticipated promotions, if any. If there are more unanticipated + // promotions than fit in reserved memory, they will be deferred until a future GC pass. + size_t total_promotion_reserve = young_advance_promoted_reserve_used + excess_old; + + old_generation->set_promoted_reserve(total_promotion_reserve); + old_generation->reset_promoted_expended(); +} size_t ShenandoahGenerationalHeuristics::add_preselected_regions_to_collection_set(ShenandoahCollectionSet* cset, const RegionData* data, diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp index 9b4c93af9b4..74d657feab7 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.hpp @@ -29,6 +29,9 @@ #include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" class ShenandoahGeneration; +class ShenandoahHeap; +class ShenandoahCollectionSet; +class RegionData; /* * This class serves as the base class for heuristics used to trigger and @@ -44,10 +47,42 @@ class ShenandoahGenerationalHeuristics : public ShenandoahAdaptiveHeuristics { public: explicit ShenandoahGenerationalHeuristics(ShenandoahGeneration* generation); - size_t choose_collection_set(ShenandoahCollectionSet* collection_set) override; + void choose_collection_set(ShenandoahCollectionSet* collection_set) override; + +private: + // Compute evacuation budgets prior to choosing collection set. + void compute_evacuation_budgets(ShenandoahHeap* const heap); + + // Preselect for possible inclusion into the collection set exactly the most + // garbage-dense regions, including those that satisfy criteria 1 & 2 below, + // and whose live bytes will fit within old_available budget: + // Criterion 1. region age >= tenuring threshold + // Criterion 2. region garbage percentage > old garbage threshold + // + // Identifies regions eligible for promotion in place, + // being those of at least tenuring_threshold age that have lower garbage + // density. + // + // Updates promotion_potential and pad_for_promote_in_place fields + // of the heap. Returns bytes of live object memory in the preselected + // regions, which are marked in the preselected_regions() indicator + // array of the heap's collection set, which should be initialized + // to false. + size_t select_aged_regions(const size_t old_promotion_reserve); + + // Filter and sort remaining regions before adding to collection set. + void filter_regions(ShenandoahCollectionSet* collection_set); + + // Adjust evacuation budgets after choosing collection set. The argument regions_to_xfer + // represents regions to be transferred to old based on decisions made in top_off_collection_set() + void adjust_evacuation_budgets(ShenandoahHeap* const heap, + ShenandoahCollectionSet* const collection_set); + protected: ShenandoahGeneration* _generation; + size_t _add_regions_to_old; + size_t add_preselected_regions_to_collection_set(ShenandoahCollectionSet* cset, const RegionData* data, size_t size) const; diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp index f47371c14d5..dd2ad28aa4b 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp @@ -36,14 +36,13 @@ ShenandoahGlobalHeuristics::ShenandoahGlobalHeuristics(ShenandoahGlobalGeneratio } -size_t ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +void ShenandoahGlobalHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { // Better select garbage-first regions - QuickSort::sort(data, (int) size, compare_by_garbage); + QuickSort::sort(data, size, compare_by_garbage); choose_global_collection_set(cset, data, size, actual_free, 0 /* cur_young_garbage */); - return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp index e0513f60da9..1f95f75c521 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp @@ -39,9 +39,9 @@ class ShenandoahGlobalHeuristics : public ShenandoahGenerationalHeuristics { public: ShenandoahGlobalHeuristics(ShenandoahGlobalGeneration* generation); - size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; private: void choose_global_collection_set(ShenandoahCollectionSet* cset, diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp index aeb64b6f1df..8fc744112bf 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.cpp @@ -72,7 +72,7 @@ ShenandoahHeuristics::~ShenandoahHeuristics() { FREE_C_HEAP_ARRAY(RegionGarbage, _region_data); } -size_t ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { +void ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* collection_set) { ShenandoahHeap* heap = ShenandoahHeap::heap(); assert(collection_set->is_empty(), "Must be empty"); @@ -154,7 +154,6 @@ size_t ShenandoahHeuristics::choose_collection_set(ShenandoahCollectionSet* coll choose_collection_set_from_regiondata(collection_set, candidates, cand_idx, immediate_garbage + free); } collection_set->summarize(total_garbage, immediate_garbage, immediate_regions); - return 0; } void ShenandoahHeuristics::record_cycle_start() { diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp index ae34a9743a9..633c4e87126 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahHeuristics.hpp @@ -183,12 +183,10 @@ protected: static int compare_by_garbage(RegionData a, RegionData b); - // This is a helper function to choose_collection_set(), returning the number of regions that need to be transferred to - // the old reserve from the young reserve in order to effectively evacuate the chosen collection set. In non-generational - // mode, the return value is 0. - virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, - RegionData* data, size_t data_size, - size_t free) = 0; + // This is a helper function to choose_collection_set() + virtual void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, + size_t free) = 0; void adjust_penalty(intx step); @@ -238,7 +236,7 @@ public: // Choose the collection set, returning the number of regions that need to be transferred to the old reserve from the young // reserve in order to effectively evacuate the chosen collection set. In non-generational mode, the return value is 0. - virtual size_t choose_collection_set(ShenandoahCollectionSet* collection_set); + virtual void choose_collection_set(ShenandoahCollectionSet* collection_set); virtual bool can_unload_classes(); diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp index f47d0cbe819..e0cab781674 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.cpp @@ -884,9 +884,8 @@ bool ShenandoahOldHeuristics::is_experimental() { return true; } -size_t ShenandoahOldHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, - ShenandoahHeuristics::RegionData* data, - size_t data_size, size_t free) { +void ShenandoahOldHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + ShenandoahHeuristics::RegionData* data, + size_t data_size, size_t free) { ShouldNotReachHere(); - return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp index 97a5b1ebf24..fc7a35aa6c8 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp @@ -155,8 +155,8 @@ private: void set_trigger_if_old_is_overgrown(); protected: - size_t - choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, RegionData* data, size_t data_size, size_t free) override; + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, size_t free) override; // This internal helper routine adds as many mixed evacuation candidate regions as fit within the old-gen evacuation budget // to the collection set. This may be called twice to prepare for any given mixed evacuation cycle, the first time with diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp index d4a38278161..b5e9cc433ea 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.cpp @@ -50,9 +50,9 @@ bool ShenandoahPassiveHeuristics::should_degenerate_cycle() { return ShenandoahDegeneratedGC; } -size_t ShenandoahPassiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) { +void ShenandoahPassiveHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) { assert(ShenandoahDegeneratedGC, "This path is only taken for Degenerated GC"); // Do not select too large CSet that would overflow the available free space. @@ -76,5 +76,4 @@ size_t ShenandoahPassiveHeuristics::choose_collection_set_from_regiondata(Shenan cset->add_region(r); } } - return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp index 7a64fad7cc9..3cb85f5d05f 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp @@ -40,19 +40,19 @@ class ShenandoahPassiveHeuristics : public ShenandoahHeuristics { public: ShenandoahPassiveHeuristics(ShenandoahSpaceInfo* space_info); - virtual bool should_start_gc(); + bool should_start_gc() override; - virtual bool should_unload_classes(); + bool should_unload_classes() override; - virtual bool should_degenerate_cycle(); + bool should_degenerate_cycle() override; - virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, - RegionData* data, size_t data_size, - size_t free); + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* set, + RegionData* data, size_t data_size, + size_t free) override; - virtual const char* name() { return "Passive"; } - virtual bool is_diagnostic() { return true; } - virtual bool is_experimental() { return false; } + const char* name() override { return "Passive"; } + bool is_diagnostic() override { return true; } + bool is_experimental() override { return false; } }; #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHPASSIVEHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp index 3843e434781..5f384f3dc73 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp @@ -37,8 +37,6 @@ ShenandoahStaticHeuristics::ShenandoahStaticHeuristics(ShenandoahSpaceInfo* spac SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahImplicitGCInvokesConcurrent); } -ShenandoahStaticHeuristics::~ShenandoahStaticHeuristics() {} - bool ShenandoahStaticHeuristics::should_start_gc() { size_t capacity = ShenandoahHeap::heap()->soft_max_capacity(); size_t available = _space_info->soft_mutator_available(); @@ -59,9 +57,9 @@ bool ShenandoahStaticHeuristics::should_start_gc() { return ShenandoahHeuristics::should_start_gc(); } -size_t ShenandoahStaticHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t free) { +void ShenandoahStaticHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t free) { size_t threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100; for (size_t idx = 0; idx < size; idx++) { @@ -70,5 +68,4 @@ size_t ShenandoahStaticHeuristics::choose_collection_set_from_regiondata(Shenand cset->add_region(r); } } - return 0; } diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp index 27dc3c8e0ae..b1514b55e5a 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp @@ -34,19 +34,17 @@ */ class ShenandoahStaticHeuristics : public ShenandoahHeuristics { public: - ShenandoahStaticHeuristics(ShenandoahSpaceInfo* space_info); + explicit ShenandoahStaticHeuristics(ShenandoahSpaceInfo* space_info); - virtual ~ShenandoahStaticHeuristics(); + bool should_start_gc() override; - virtual bool should_start_gc(); + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t free) override; - virtual size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t free); - - virtual const char* name() { return "Static"; } - virtual bool is_diagnostic() { return false; } - virtual bool is_experimental() { return false; } + const char* name() override { return "Static"; } + bool is_diagnostic() override { return false; } + bool is_experimental() override { return false; } }; #endif // SHARE_GC_SHENANDOAH_HEURISTICS_SHENANDOAHSTATICHEURISTICS_HPP diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp index 01c3873df72..beff2200d90 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp @@ -37,7 +37,7 @@ ShenandoahYoungHeuristics::ShenandoahYoungHeuristics(ShenandoahYoungGeneration* } -size_t ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, +void ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, RegionData* data, size_t size, size_t actual_free) { // See comments in ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(): @@ -52,7 +52,7 @@ size_t ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(Shenando bool need_to_finalize_mixed = heap->old_generation()->heuristics()->prime_collection_set(cset); // Better select garbage-first regions - QuickSort::sort(data, (int) size, compare_by_garbage); + QuickSort::sort(data, size, compare_by_garbage); size_t cur_young_garbage = add_preselected_regions_to_collection_set(cset, data, size); @@ -62,12 +62,10 @@ size_t ShenandoahYoungHeuristics::choose_collection_set_from_regiondata(Shenando // enough consolidated garbage to make effective use of young-gen evacuation reserve. If there is still // young-gen reserve available following selection of the young-gen collection set, see if we can use // this memory to expand the old-gen evacuation collection set. - size_t add_regions_to_old; - need_to_finalize_mixed |= heap->old_generation()->heuristics()->top_off_collection_set(add_regions_to_old); + need_to_finalize_mixed |= heap->old_generation()->heuristics()->top_off_collection_set(_add_regions_to_old); if (need_to_finalize_mixed) { heap->old_generation()->heuristics()->finalize_mixed_evacs(); } - return add_regions_to_old; } void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollectionSet* cset, diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp index 85587887663..b9d64059680 100644 --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp @@ -38,9 +38,9 @@ public: explicit ShenandoahYoungHeuristics(ShenandoahYoungGeneration* generation); - size_t choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, - RegionData* data, size_t size, - size_t actual_free) override; + void choose_collection_set_from_regiondata(ShenandoahCollectionSet* cset, + RegionData* data, size_t size, + size_t actual_free) override; bool should_start_gc() override; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp index cdc7e1a328a..ddb50ee0020 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp @@ -24,7 +24,6 @@ */ #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp" -#include "gc/shenandoah/shenandoahCollectionSetPreselector.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahGeneration.hpp" @@ -245,506 +244,6 @@ void ShenandoahGeneration::parallel_heap_region_iterate_free(ShenandoahHeapRegio ShenandoahHeap::heap()->parallel_heap_region_iterate(cl); } -void ShenandoahGeneration::compute_evacuation_budgets(ShenandoahHeap* const heap) { - shenandoah_assert_generational(); - - ShenandoahOldGeneration* const old_generation = heap->old_generation(); - ShenandoahYoungGeneration* const young_generation = heap->young_generation(); - const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - - // During initialization and phase changes, it is more likely that fewer objects die young and old-gen - // memory is not yet full (or is in the process of being replaced). During these times especially, it - // is beneficial to loan memory from old-gen to young-gen during the evacuation and update-refs phases - // of execution. - - // Calculate EvacuationReserve before PromotionReserve. Evacuation is more critical than promotion. - // If we cannot evacuate old-gen, we will not be able to reclaim old-gen memory. Promotions are less - // critical. If we cannot promote, there may be degradation of young-gen memory because old objects - // accumulate there until they can be promoted. This increases the young-gen marking and evacuation work. - - // First priority is to reclaim the easy garbage out of young-gen. - - // maximum_young_evacuation_reserve is upper bound on memory to be evacuated into young Collector Reserve. This is - // bounded at the end of previous GC cycle, based on available memory and balancing of evacuation to old and young. - size_t maximum_young_evacuation_reserve = young_generation->get_evacuation_reserve(); - - // maximum_old_evacuation_reserve is an upper bound on memory evacuated from old and evacuated to old (promoted), - // clamped by the old generation space available. - // - // Here's the algebra. - // Let SOEP = ShenandoahOldEvacPercent, - // OE = old evac, - // YE = young evac, and - // TE = total evac = OE + YE - // By definition: - // SOEP/100 = OE/TE - // = OE/(OE+YE) - // => SOEP/(100-SOEP) = OE/((OE+YE)-OE) // componendo-dividendo: If a/b = c/d, then a/(b-a) = c/(d-c) - // = OE/YE - // => OE = YE*SOEP/(100-SOEP) - - // We have to be careful in the event that SOEP is set to 100 by the user. - assert(ShenandoahOldEvacPercent <= 100, "Error"); - const size_t old_available = old_generation->available(); - const size_t maximum_old_evacuation_reserve = (ShenandoahOldEvacPercent == 100) ? - old_available : MIN2((maximum_young_evacuation_reserve * ShenandoahOldEvacPercent) / (100 - ShenandoahOldEvacPercent), - old_available); - - // In some cases, maximum_old_reserve < old_available (when limited by ShenandoahOldEvacPercent) - // This limit affects mixed evacuations, but does not affect promotions. - - // Second priority is to reclaim garbage out of old-gen if there are old-gen collection candidates. Third priority - // is to promote as much as we have room to promote. However, if old-gen memory is in short supply, this means young - // GC is operating under "duress" and was unable to transfer the memory that we would normally expect. In this case, - // old-gen will refrain from compacting itself in order to allow a quicker young-gen cycle (by avoiding the update-refs - // through ALL of old-gen). If there is some memory available in old-gen, we will use this for promotions as promotions - // do not add to the update-refs burden of GC. - - size_t old_evacuation_reserve, old_promo_reserve; - if (is_global()) { - // Global GC is typically triggered by user invocation of System.gc(), and typically indicates that there is lots - // of garbage to be reclaimed because we are starting a new phase of execution. Marking for global GC may take - // significantly longer than typical young marking because we must mark through all old objects. To expedite - // evacuation and update-refs, we give emphasis to reclaiming garbage first, wherever that garbage is found. - // Global GC will adjust generation sizes to accommodate the collection set it chooses. - - // Use remnant of old_available to hold promotions. - old_promo_reserve = old_available - maximum_old_evacuation_reserve; - - // Dedicate all available old memory to old_evacuation reserve. This may be small, because old-gen is only - // expanded based on an existing mixed evacuation workload at the end of the previous GC cycle. We'll expand - // the budget for evacuation of old during GLOBAL cset selection. - old_evacuation_reserve = maximum_old_evacuation_reserve; - } else if (old_generation->has_unprocessed_collection_candidates()) { - // We reserved all old-gen memory at end of previous GC to hold anticipated evacuations to old-gen. If this is - // mixed evacuation, reserve all of this memory for compaction of old-gen and do not promote. Prioritize compaction - // over promotion in order to defragment OLD so that it will be better prepared to efficiently receive promoted memory. - old_evacuation_reserve = maximum_old_evacuation_reserve; - old_promo_reserve = old_available - maximum_old_evacuation_reserve; - } else { - // Make all old-evacuation memory for promotion, but if we can't use it all for promotion, we'll allow some evacuation. - old_evacuation_reserve = old_available - maximum_old_evacuation_reserve; - old_promo_reserve = maximum_old_evacuation_reserve; - } - assert(old_evacuation_reserve <= old_available, "Error"); - - - // We see too many old-evacuation failures if we force ourselves to evacuate into regions that are not initially empty. - // So we limit the old-evacuation reserve to unfragmented memory. Even so, old-evacuation is free to fill in nooks and - // crannies within existing partially used regions and it generally tries to do so. - const size_t old_free_unfragmented = old_generation->free_unaffiliated_regions() * region_size_bytes; - if (old_evacuation_reserve > old_free_unfragmented) { - const size_t delta = old_evacuation_reserve - old_free_unfragmented; - old_evacuation_reserve -= delta; - // Let promo consume fragments of old-gen memory - old_promo_reserve += delta; - } - - // If is_global(), we let garbage-first heuristic determine cset membership. Otherwise, we give priority - // to tenurable regions by preselecting regions for promotion by evacuation (obtaining the live data to seed promoted_reserve). - // This also identifies regions that will be promoted in place. These use the tenuring threshold. - const size_t consumed_by_advance_promotion = select_aged_regions(is_global()? 0: old_promo_reserve); - assert(consumed_by_advance_promotion <= old_promo_reserve, "Do not promote more than budgeted"); - - // The young evacuation reserve can be no larger than young_unaffiliated. Planning to evacuate into partially consumed - // young regions is doomed to failure if any of those partially consumed regions is selected for the collection set. - size_t young_unaffiliated = young_generation->free_unaffiliated_regions() * region_size_bytes; - - // If any regions have been selected for promotion in place, this has the effect of decreasing available within mutator - // and collector partitions, due to padding of remnant memory within each promoted in place region. This will affect - // young_evacuation_reserve but not old_evacuation_reserve or consumed_by_advance_promotion. So recompute. - size_t young_evacuation_reserve = MIN2(maximum_young_evacuation_reserve, young_unaffiliated); - - // Note that unused old_promo_reserve might not be entirely consumed_by_advance_promotion. Do not transfer this - // to old_evacuation_reserve because this memory is likely very fragmented, and we do not want to increase the likelihood - // of old evacuation failure. Leave this memory in the promoted reserve as it may be targeted by opportunistic - // promotions (found during evacuation of young regions). - young_generation->set_evacuation_reserve(young_evacuation_reserve); - old_generation->set_evacuation_reserve(old_evacuation_reserve); - old_generation->set_promoted_reserve(old_promo_reserve); - - // There is no need to expand OLD because all memory used here was set aside at end of previous GC, except in the - // case of a GLOBAL gc. During choose_collection_set() of GLOBAL, old will be expanded on demand. -} - -// Having chosen the collection set, adjust the budgets for generational mode based on its composition. Note -// that young_generation->available() now knows about recently discovered immediate garbage. -void ShenandoahGeneration::adjust_evacuation_budgets(ShenandoahHeap* const heap, - ShenandoahCollectionSet* const collection_set, size_t add_regions_to_old) { - shenandoah_assert_generational(); - // We may find that old_evacuation_reserve and/or loaned_for_young_evacuation are not fully consumed, in which case we may - // be able to increase regions_available_to_loan - - // The role of adjust_evacuation_budgets() is to compute the correct value of regions_available_to_loan and to make - // effective use of this memory, including the remnant memory within these regions that may result from rounding loan to - // integral number of regions. Excess memory that is available to be loaned is applied to an allocation supplement, - // which allows mutators to allocate memory beyond the current capacity of young-gen on the promise that the loan - // will be repaid as soon as we finish updating references for the recently evacuated collection set. - - // We cannot recalculate regions_available_to_loan by simply dividing old_generation->available() by region_size_bytes - // because the available memory may be distributed between many partially occupied regions that are already holding old-gen - // objects. Memory in partially occupied regions is not "available" to be loaned. Note that an increase in old-gen - // available that results from a decrease in memory consumed by old evacuation is not necessarily available to be loaned - // to young-gen. - - const size_t region_size_bytes = ShenandoahHeapRegion::region_size_bytes(); - ShenandoahOldGeneration* const old_generation = heap->old_generation(); - ShenandoahYoungGeneration* const young_generation = heap->young_generation(); - - const size_t old_evacuated = collection_set->get_live_bytes_in_old_regions(); - size_t old_evacuated_committed = (size_t) (ShenandoahOldEvacWaste * double(old_evacuated)); - size_t old_evacuation_reserve = old_generation->get_evacuation_reserve(); - - if (old_evacuated_committed > old_evacuation_reserve) { - // This should only happen due to round-off errors when enforcing ShenandoahOldEvacWaste - assert(old_evacuated_committed <= (33 * old_evacuation_reserve) / 32, - "Round-off errors should be less than 3.125%%, committed: %zu, reserved: %zu", - old_evacuated_committed, old_evacuation_reserve); - old_evacuated_committed = old_evacuation_reserve; - // Leave old_evac_reserve as previously configured - } else if (old_evacuated_committed < old_evacuation_reserve) { - // This happens if the old-gen collection consumes less than full budget. - log_debug(gc, cset)("Shrinking old evac reserve to match old_evac_commited: " PROPERFMT, - PROPERFMTARGS(old_evacuated_committed)); - old_evacuation_reserve = old_evacuated_committed; - old_generation->set_evacuation_reserve(old_evacuation_reserve); - } - - size_t young_advance_promoted = collection_set->get_live_bytes_in_tenurable_regions(); - size_t young_advance_promoted_reserve_used = (size_t) (ShenandoahPromoEvacWaste * double(young_advance_promoted)); - - size_t young_evacuated = collection_set->get_live_bytes_in_untenurable_regions(); - size_t young_evacuated_reserve_used = (size_t) (ShenandoahEvacWaste * double(young_evacuated)); - - size_t total_young_available = young_generation->available_with_reserve() - add_regions_to_old * region_size_bytes;; - assert(young_evacuated_reserve_used <= total_young_available, "Cannot evacuate (%zu) more than is available in young (%zu)", - young_evacuated_reserve_used, total_young_available); - young_generation->set_evacuation_reserve(young_evacuated_reserve_used); - - // We have not yet rebuilt the free set. Some of the memory that is thought to be avaiable within old may no - // longer be available if that memory had been free within regions that were selected for the collection set. - // Make the necessary adjustments to old_available. - size_t old_available = - old_generation->available() + add_regions_to_old * region_size_bytes - collection_set->get_old_available_bytes_collected(); - - // Now that we've established the collection set, we know how much memory is really required by old-gen for evacuation - // and promotion reserves. Try shrinking OLD now in case that gives us a bit more runway for mutator allocations during - // evac and update phases. - size_t old_consumed = old_evacuated_committed + young_advance_promoted_reserve_used; - - if (old_available < old_consumed) { - // This can happen due to round-off errors when adding the results of truncated integer arithmetic. - // We've already truncated old_evacuated_committed. Truncate young_advance_promoted_reserve_used here. - - assert(young_advance_promoted_reserve_used <= (33 * (old_available - old_evacuated_committed)) / 32, - "Round-off errors should be less than 3.125%%, committed: %zu, reserved: %zu", - young_advance_promoted_reserve_used, old_available - old_evacuated_committed); - if (old_available > old_evacuated_committed) { - young_advance_promoted_reserve_used = old_available - old_evacuated_committed; - } else { - young_advance_promoted_reserve_used = 0; - old_evacuated_committed = old_available; - } - // TODO: reserve for full promotion reserve, not just for advance (preselected) promotion - old_consumed = old_evacuated_committed + young_advance_promoted_reserve_used; - } - - assert(old_available >= old_consumed, "Cannot consume (%zu) more than is available (%zu)", - old_consumed, old_available); - size_t excess_old = old_available - old_consumed; - size_t unaffiliated_old_regions = old_generation->free_unaffiliated_regions() + add_regions_to_old; - size_t unaffiliated_old = unaffiliated_old_regions * region_size_bytes; - assert(unaffiliated_old >= old_evacuated_committed, "Do not evacuate (%zu) more than unaffiliated old (%zu)", - old_evacuated_committed, unaffiliated_old); - - // Make sure old_evac_committed is unaffiliated - if (old_evacuated_committed > 0) { - if (unaffiliated_old > old_evacuated_committed) { - size_t giveaway = unaffiliated_old - old_evacuated_committed; - size_t giveaway_regions = giveaway / region_size_bytes; // round down - if (giveaway_regions > 0) { - excess_old = MIN2(excess_old, giveaway_regions * region_size_bytes); - } else { - excess_old = 0; - } - } else { - excess_old = 0; - } - } - - // If we find that OLD has excess regions, give them back to YOUNG now to reduce likelihood we run out of allocation - // runway during evacuation and update-refs. We may make further adjustments to balance. - ssize_t add_regions_to_young = 0; - if (excess_old > unaffiliated_old) { - // we can give back unaffiliated_old (all of unaffiliated is excess) - if (unaffiliated_old_regions > 0) { - add_regions_to_young = unaffiliated_old_regions; - } - } else if (unaffiliated_old_regions > 0) { - // excess_old < unaffiliated old: we can give back MIN(excess_old/region_size_bytes, unaffiliated_old_regions) - size_t excess_regions = excess_old / region_size_bytes; - add_regions_to_young = MIN2(excess_regions, unaffiliated_old_regions); - } - - if (add_regions_to_young > 0) { - assert(excess_old >= add_regions_to_young * region_size_bytes, "Cannot xfer more than excess old"); - excess_old -= add_regions_to_young * region_size_bytes; - log_debug(gc, ergo)("Before start of evacuation, total_promotion reserve is young_advance_promoted_reserve: %zu " - "plus excess: old: %zu", young_advance_promoted_reserve_used, excess_old); - } - - // Add in the excess_old memory to hold unanticipated promotions, if any. If there are more unanticipated - // promotions than fit in reserved memory, they will be deferred until a future GC pass. - size_t total_promotion_reserve = young_advance_promoted_reserve_used + excess_old; - - old_generation->set_promoted_reserve(total_promotion_reserve); - old_generation->reset_promoted_expended(); -} - -typedef struct { - ShenandoahHeapRegion* _region; - size_t _live_data; -} AgedRegionData; - -static int compare_by_aged_live(AgedRegionData a, AgedRegionData b) { - if (a._live_data < b._live_data) - return -1; - else if (a._live_data > b._live_data) - return 1; - else return 0; -} - -inline void assert_no_in_place_promotions() { -#ifdef ASSERT - class ShenandoahNoInPlacePromotions : public ShenandoahHeapRegionClosure { - public: - void heap_region_do(ShenandoahHeapRegion *r) override { - assert(r->get_top_before_promote() == nullptr, - "Region %zu should not be ready for in-place promotion", r->index()); - } - } cl; - ShenandoahHeap::heap()->heap_region_iterate(&cl); -#endif -} - -// Preselect for inclusion into the collection set all regions whose age is at or above tenure age and for which the -// garbage percentage exceeds a dynamically adjusted threshold (known as the old-garbage threshold percentage). We -// identify these regions by setting the appropriate entry of the collection set's preselected regions array to true. -// All entries are initialized to false before calling this function. -// -// During the subsequent selection of the collection set, we give priority to these promotion set candidates. -// Without this prioritization, we found that the aged regions tend to be ignored because they typically have -// much less garbage and much more live data than the recently allocated "eden" regions. When aged regions are -// repeatedly excluded from the collection set, the amount of live memory within the young generation tends to -// accumulate and this has the undesirable side effect of causing young-generation collections to require much more -// CPU and wall-clock time. -// -// A second benefit of treating aged regions differently than other regions during collection set selection is -// that this allows us to more accurately budget memory to hold the results of evacuation. Memory for evacuation -// of aged regions must be reserved in the old generation. Memory for evacuation of all other regions must be -// reserved in the young generation. -size_t ShenandoahGeneration::select_aged_regions(const size_t old_promotion_reserve) { - - // There should be no regions configured for subsequent in-place-promotions carried over from the previous cycle. - assert_no_in_place_promotions(); - - auto const heap = ShenandoahGenerationalHeap::heap(); - ShenandoahFreeSet* free_set = heap->free_set(); - bool* const candidate_regions_for_promotion_by_copy = heap->collection_set()->preselected_regions(); - ShenandoahMarkingContext* const ctx = heap->marking_context(); - - const size_t old_garbage_threshold = - (ShenandoahHeapRegion::region_size_bytes() * heap->old_generation()->heuristics()->get_old_garbage_threshold()) / 100; - - const size_t pip_used_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahGenerationalMinPIPUsage) / 100; - - size_t promo_potential = 0; - size_t candidates = 0; - - // Tracks the padding of space above top in regions eligible for promotion in place - size_t promote_in_place_pad = 0; - - // Sort the promotion-eligible regions in order of increasing live-data-bytes so that we can first reclaim regions that require - // less evacuation effort. This prioritizes garbage first, expanding the allocation pool early before we reclaim regions that - // have more live data. - const idx_t num_regions = heap->num_regions(); - - ResourceMark rm; - AgedRegionData* sorted_regions = NEW_RESOURCE_ARRAY(AgedRegionData, num_regions); - - ShenandoahFreeSet* freeset = heap->free_set(); - - // Any region that is to be promoted in place needs to be retired from its Collector or Mutator partition. - idx_t pip_low_collector_idx = freeset->max_regions(); - idx_t pip_high_collector_idx = -1; - idx_t pip_low_mutator_idx = freeset->max_regions(); - idx_t pip_high_mutator_idx = -1; - size_t collector_regions_to_pip = 0; - size_t mutator_regions_to_pip = 0; - - size_t pip_mutator_regions = 0; - size_t pip_collector_regions = 0; - size_t pip_mutator_bytes = 0; - size_t pip_collector_bytes = 0; - - for (idx_t i = 0; i < num_regions; i++) { - ShenandoahHeapRegion* const r = heap->get_region(i); - if (r->is_empty() || !r->has_live() || !r->is_young() || !r->is_regular()) { - // skip over regions that aren't regular young with some live data - continue; - } - if (heap->is_tenurable(r)) { - if ((r->garbage() < old_garbage_threshold) && (r->used() > pip_used_threshold)) { - // We prefer to promote this region in place because it has a small amount of garbage and a large usage. - HeapWord* tams = ctx->top_at_mark_start(r); - HeapWord* original_top = r->top(); - if (!heap->is_concurrent_old_mark_in_progress() && tams == original_top) { - // No allocations from this region have been made during concurrent mark. It meets all the criteria - // for in-place-promotion. Though we only need the value of top when we fill the end of the region, - // we use this field to indicate that this region should be promoted in place during the evacuation - // phase. - r->save_top_before_promote(); - size_t remnant_bytes = r->free(); - size_t remnant_words = remnant_bytes / HeapWordSize; - assert(ShenandoahHeap::min_fill_size() <= PLAB::min_size(), "Implementation makes invalid assumptions"); - if (remnant_words >= ShenandoahHeap::min_fill_size()) { - ShenandoahHeap::fill_with_object(original_top, remnant_words); - // Fill the remnant memory within this region to assure no allocations prior to promote in place. Otherwise, - // newly allocated objects will not be parsable when promote in place tries to register them. Furthermore, any - // new allocations would not necessarily be eligible for promotion. This addresses both issues. - r->set_top(r->end()); - // The region r is either in the Mutator or Collector partition if remnant_words > heap()->plab_min_size. - // Otherwise, the region is in the NotFree partition. - ShenandoahFreeSetPartitionId p = free_set->membership(i); - if (p == ShenandoahFreeSetPartitionId::Mutator) { - mutator_regions_to_pip++; - if (i < pip_low_mutator_idx) { - pip_low_mutator_idx = i; - } - if (i > pip_high_mutator_idx) { - pip_high_mutator_idx = i; - } - pip_mutator_regions++; - pip_mutator_bytes += remnant_bytes; - } else if (p == ShenandoahFreeSetPartitionId::Collector) { - collector_regions_to_pip++; - if (i < pip_low_collector_idx) { - pip_low_collector_idx = i; - } - if (i > pip_high_collector_idx) { - pip_high_collector_idx = i; - } - pip_collector_regions++; - pip_collector_bytes += remnant_bytes; - } else { - assert((p == ShenandoahFreeSetPartitionId::NotFree) && (remnant_words < heap->plab_min_size()), - "Should be NotFree if not in Collector or Mutator partitions"); - // In this case, the memory is already counted as used and the region has already been retired. There is - // no need for further adjustments to used. Further, the remnant memory for this region will not be - // unallocated or made available to OldCollector after pip. - remnant_bytes = 0; - } - promote_in_place_pad += remnant_bytes; - free_set->prepare_to_promote_in_place(i, remnant_bytes); - } else { - // Since the remnant is so small that this region has already been retired, we don't have to worry about any - // accidental allocations occurring within this region before the region is promoted in place. - - // This region was already not in the Collector or Mutator set, so no need to remove it. - assert(free_set->membership(i) == ShenandoahFreeSetPartitionId::NotFree, "sanity"); - } - } - // Else, we do not promote this region (either in place or by copy) because it has received new allocations. - - // During evacuation, we exclude from promotion regions for which age > tenure threshold, garbage < garbage-threshold, - // used > pip_used_threshold, and get_top_before_promote() != tams - } else { - // Record this promotion-eligible candidate region. After sorting and selecting the best candidates below, - // we may still decide to exclude this promotion-eligible region from the current collection set. If this - // happens, we will consider this region as part of the anticipated promotion potential for the next GC - // pass; see further below. - sorted_regions[candidates]._region = r; - sorted_regions[candidates++]._live_data = r->get_live_data_bytes(); - } - } else { - // We only evacuate & promote objects from regular regions whose garbage() is above old-garbage-threshold. - // Objects in tenure-worthy regions with less garbage are promoted in place. These take a different path to - // old-gen. Regions excluded from promotion because their garbage content is too low (causing us to anticipate that - // the region would be promoted in place) may be eligible for evacuation promotion by the time promotion takes - // place during a subsequent GC pass because more garbage is found within the region between now and then. This - // should not happen if we are properly adapting the tenure age. The theory behind adaptive tenuring threshold - // is to choose the youngest age that demonstrates no "significant" further loss of population since the previous - // age. If not this, we expect the tenure age to demonstrate linear population decay for at least two population - // samples, whereas we expect to observe exponential population decay for ages younger than the tenure age. - // - // In the case that certain regions which were anticipated to be promoted in place need to be promoted by - // evacuation, it may be the case that there is not sufficient reserve within old-gen to hold evacuation of - // these regions. The likely outcome is that these regions will not be selected for evacuation or promotion - // in the current cycle and we will anticipate that they will be promoted in the next cycle. This will cause - // us to reserve more old-gen memory so that these objects can be promoted in the subsequent cycle. - if (heap->is_aging_cycle() && heap->age_census()->is_tenurable(r->age() + 1)) { - if (r->garbage() >= old_garbage_threshold) { - promo_potential += r->get_live_data_bytes(); - } - } - } - // Note that we keep going even if one region is excluded from selection. - // Subsequent regions may be selected if they have smaller live data. - } - - if (pip_mutator_regions + pip_collector_regions > 0) { - freeset->account_for_pip_regions(pip_mutator_regions, pip_mutator_bytes, pip_collector_regions, pip_collector_bytes); - } - - // Retire any regions that have been selected for promote in place - if (collector_regions_to_pip > 0) { - freeset->shrink_interval_if_range_modifies_either_boundary(ShenandoahFreeSetPartitionId::Collector, - pip_low_collector_idx, pip_high_collector_idx, - collector_regions_to_pip); - } - if (mutator_regions_to_pip > 0) { - freeset->shrink_interval_if_range_modifies_either_boundary(ShenandoahFreeSetPartitionId::Mutator, - pip_low_mutator_idx, pip_high_mutator_idx, - mutator_regions_to_pip); - } - - // Sort in increasing order according to live data bytes. Note that candidates represents the number of regions - // that qualify to be promoted by evacuation. - size_t old_consumed = 0; - if (candidates > 0) { - size_t selected_regions = 0; - size_t selected_live = 0; - QuickSort::sort(sorted_regions, candidates, compare_by_aged_live); - for (size_t i = 0; i < candidates; i++) { - ShenandoahHeapRegion* const region = sorted_regions[i]._region; - const size_t region_live_data = sorted_regions[i]._live_data; - const size_t promotion_need = (size_t) (region_live_data * ShenandoahPromoEvacWaste); - if (old_consumed + promotion_need <= old_promotion_reserve) { - old_consumed += promotion_need; - candidate_regions_for_promotion_by_copy[region->index()] = true; - selected_regions++; - selected_live += region_live_data; - } else { - // We rejected this promotable region from the collection set because we had no room to hold its copy. - // Add this region to promo potential for next GC. - promo_potential += region_live_data; - assert(!candidate_regions_for_promotion_by_copy[region->index()], "Shouldn't be selected"); - } - // We keep going even if one region is excluded from selection because we need to accumulate all eligible - // regions that are not preselected into promo_potential - } - log_debug(gc, ergo)("Preselected %zu regions containing " PROPERFMT " live data," - " consuming: " PROPERFMT " of budgeted: " PROPERFMT, - selected_regions, PROPERFMTARGS(selected_live), PROPERFMTARGS(old_consumed), PROPERFMTARGS(old_promotion_reserve)); - } - - log_info(gc, ergo)("Promotion potential of aged regions with sufficient garbage: " PROPERFMT, PROPERFMTARGS(promo_potential)); - - heap->old_generation()->set_pad_for_promote_in_place(promote_in_place_pad); - heap->old_generation()->set_promotion_potential(promo_potential); - return old_consumed; -} - void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahCollectionSet* collection_set = heap->collection_set(); @@ -798,34 +297,7 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) { collection_set->clear(); ShenandoahHeapLocker locker(heap->lock()); - if (is_generational) { - // Seed the collection set with resource area-allocated - // preselected regions, which are removed when we exit this scope. - ShenandoahCollectionSetPreselector preselector(collection_set, heap->num_regions()); - - // Find the amount that will be promoted, regions that will be promoted in - // place, and preselected older regions that will be promoted by evacuation. - compute_evacuation_budgets(heap); - - // Choose the collection set, including the regions preselected above for promotion into the old generation. - size_t add_regions_to_old = _heuristics->choose_collection_set(collection_set); - // Even if collection_set->is_empty(), we want to adjust budgets, making reserves available to mutator. - adjust_evacuation_budgets(heap, collection_set, add_regions_to_old); - if (is_global()) { - // We have just chosen a collection set for a global cycle. The mark bitmap covering old regions is complete, so - // the remembered set scan can use that to avoid walking into garbage. When the next old mark begins, we will - // use the mark bitmap to make the old regions parsable by coalescing and filling any unmarked objects. Thus, - // we prepare for old collections by remembering which regions are old at this time. Note that any objects - // promoted into old regions will be above TAMS, and so will be considered marked. However, free regions that - // become old after this point will not be covered correctly by the mark bitmap, so we must be careful not to - // coalesce those regions. Only the old regions which are not part of the collection set at this point are - // eligible for coalescing. As implemented now, this has the side effect of possibly initiating mixed-evacuations - // after a global cycle for old regions that were not included in this collection set. - heap->old_generation()->prepare_for_mixed_collections_after_global_gc(); - } - } else { - _heuristics->choose_collection_set(collection_set); - } + _heuristics->choose_collection_set(collection_set); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp index d49e3bed5f8..946f2b91520 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp @@ -60,31 +60,6 @@ protected: ShenandoahHeuristics* _heuristics; private: - // Compute evacuation budgets prior to choosing collection set. - void compute_evacuation_budgets(ShenandoahHeap* heap); - - // Adjust evacuation budgets after choosing collection set. The argument regions_to_xfer represents regions to be - // transfered to old based on decisions made in top_off_collection_set() - void adjust_evacuation_budgets(ShenandoahHeap* heap, - ShenandoahCollectionSet* collection_set, size_t regions_to_xfer); - - // Preselect for possible inclusion into the collection set exactly the most - // garbage-dense regions, including those that satisfy criteria 1 & 2 below, - // and whose live bytes will fit within old_available budget: - // Criterion 1. region age >= tenuring threshold - // Criterion 2. region garbage percentage > old garbage threshold - // - // Identifies regions eligible for promotion in place, - // being those of at least tenuring_threshold age that have lower garbage - // density. - // - // Updates promotion_potential and pad_for_promote_in_place fields - // of the heap. Returns bytes of live object memory in the preselected - // regions, which are marked in the preselected_regions() indicator - // array of the heap's collection set, which should be initialized - // to false. - size_t select_aged_regions(size_t old_promotion_reserve); - // Return available assuming that we can allocate no more than capacity bytes within this generation. size_t available(size_t capacity) const; From d0e97307836c49291f24ae7cb1c2e9319b986f8c Mon Sep 17 00:00:00 2001 From: Eric Fang Date: Thu, 5 Feb 2026 07:58:33 +0000 Subject: [PATCH 129/215] 8372980: [VectorAPI] AArch64: Add intrinsic support for unsigned min/max reduction operations Co-authored-by: Andrew Haley Reviewed-by: aph, xgong --- src/hotspot/cpu/aarch64/aarch64_vector.ad | 224 ++++- src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 | 32 +- src/hotspot/cpu/aarch64/assembler_aarch64.hpp | 6 +- .../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 82 +- .../cpu/aarch64/c2_MacroAssembler_aarch64.hpp | 33 +- src/hotspot/share/opto/classes.hpp | 4 +- src/hotspot/share/opto/compile.cpp | 2 + src/hotspot/share/opto/vectornode.cpp | 52 ++ src/hotspot/share/opto/vectornode.hpp | 14 + test/hotspot/gtest/aarch64/aarch64-asmtest.py | 8 +- test/hotspot/gtest/aarch64/asmtest.out.h | 775 +++++++++--------- .../compiler/lib/ir_framework/IRNode.java | 12 +- .../vectorapi/VectorUMinMaxReductionTest.java | 297 +++++++ .../VectorUMinUMaxReductionBenchmark.java | 235 ++++++ 14 files changed, 1358 insertions(+), 418 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/vectorapi/VectorUMinMaxReductionTest.java create mode 100644 test/micro/org/openjdk/bench/jdk/incubator/vector/VectorUMinUMaxReductionBenchmark.java diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index 78ef121bd29..19f03d97a72 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -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, 2025, Arm Limited. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // @@ -201,6 +201,8 @@ source %{ case Op_XorReductionV: case Op_MinReductionV: case Op_MaxReductionV: + case Op_UMinReductionV: + case Op_UMaxReductionV: // Reductions with less than 8 bytes vector length are // not supported. if (length_in_bytes < 8) { @@ -383,6 +385,8 @@ source %{ return !VM_Version::use_neon_for_vector(length_in_bytes); case Op_MinReductionV: case Op_MaxReductionV: + case Op_UMinReductionV: + case Op_UMaxReductionV: // For BYTE/SHORT/INT/FLOAT/DOUBLE types, we prefer using NEON // instructions rather than SVE predicated instructions for // better performance. @@ -4218,6 +4222,224 @@ instruct reduce_minD_masked(vRegD dst, vRegD dsrc, vReg vsrc, pRegGov pg) %{ ins_pipe(pipe_slow); %} +// -------------------- Vector reduction unsigned min/max ---------------------- + +// reduction uminI + +instruct reduce_uminI_neon(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc, + vReg tmp, rFlagsReg cr) %{ + predicate(VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) && + (Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || + Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || + Matcher::vector_element_basic_type(n->in(2)) == T_INT)); + match(Set dst (UMinReductionV isrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_uminI_neon $dst, $isrc, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + __ neon_reduce_minmax_integral(this->ideal_Opcode(), $dst$$Register, bt, + $isrc$$Register, $vsrc$$FloatRegister, + length_in_bytes, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_uminI_sve(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc, + vRegD tmp, rFlagsReg cr) %{ + predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) && + (Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || + Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || + Matcher::vector_element_basic_type(n->in(2)) == T_INT)); + match(Set dst (UMinReductionV isrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_uminI_sve $dst, $isrc, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + assert(UseSVE > 0, "must be sve"); + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + assert(length_in_bytes == MaxVectorSize, "invalid vector length"); + __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt, + $isrc$$Register, $vsrc$$FloatRegister, + ptrue, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// reduction uminL + +instruct reduce_uminL_neon(iRegLNoSp dst, iRegL isrc, vReg vsrc, rFlagsReg cr) %{ + predicate(UseSVE == 0 && Matcher::vector_element_basic_type(n->in(2)) == T_LONG); + match(Set dst (UMinReductionV isrc vsrc)); + effect(TEMP_DEF dst, KILL cr); + format %{ "reduce_uminL_neon $dst, $isrc, $vsrc\t# 2L. KILL cr" %} + ins_encode %{ + __ neon_reduce_minmax_integral(this->ideal_Opcode(), $dst$$Register, T_LONG, + $isrc$$Register, $vsrc$$FloatRegister, + /* vector_length_in_bytes */ 16, fnoreg); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_uminL_sve(iRegLNoSp dst, iRegL isrc, vReg vsrc, + vRegD tmp, rFlagsReg cr) %{ + predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n->in(2)) == T_LONG); + match(Set dst (UMinReductionV isrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_uminL_sve $dst, $isrc, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + assert(length_in_bytes == MaxVectorSize, "invalid vector length"); + __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, T_LONG, + $isrc$$Register, $vsrc$$FloatRegister, + ptrue, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// reduction umin - predicated + +instruct reduce_uminI_masked(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc, pRegGov pg, + vRegD tmp, rFlagsReg cr) %{ + predicate(UseSVE > 0 && + (Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_BYTE || + Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_SHORT || + Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_INT)); + match(Set dst (UMinReductionV (Binary isrc vsrc) pg)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_uminI_masked $dst, $isrc, $pg, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt, + $isrc$$Register, $vsrc$$FloatRegister, + $pg$$PRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_uminL_masked(iRegLNoSp dst, iRegL isrc, vReg vsrc, pRegGov pg, + vRegD tmp, rFlagsReg cr) %{ + predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_LONG); + match(Set dst (UMinReductionV (Binary isrc vsrc) pg)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_uminL_masked $dst, $isrc, $pg, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt, + $isrc$$Register, $vsrc$$FloatRegister, + $pg$$PRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// reduction umaxI + +instruct reduce_umaxI_neon(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc, + vReg tmp, rFlagsReg cr) %{ + predicate(VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) && + (Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || + Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || + Matcher::vector_element_basic_type(n->in(2)) == T_INT)); + match(Set dst (UMaxReductionV isrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_umaxI_neon $dst, $isrc, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + __ neon_reduce_minmax_integral(this->ideal_Opcode(), $dst$$Register, bt, + $isrc$$Register, $vsrc$$FloatRegister, + length_in_bytes, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_umaxI_sve(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc, + vRegD tmp, rFlagsReg cr) %{ + predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(2))) && + (Matcher::vector_element_basic_type(n->in(2)) == T_BYTE || + Matcher::vector_element_basic_type(n->in(2)) == T_SHORT || + Matcher::vector_element_basic_type(n->in(2)) == T_INT)); + match(Set dst (UMaxReductionV isrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_umaxI_sve $dst, $isrc, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + assert(UseSVE > 0, "must be sve"); + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + assert(length_in_bytes == MaxVectorSize, "invalid vector length"); + __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt, + $isrc$$Register, $vsrc$$FloatRegister, + ptrue, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// reduction umaxL + +instruct reduce_umaxL_neon(iRegLNoSp dst, iRegL isrc, vReg vsrc, rFlagsReg cr) %{ + predicate(UseSVE == 0 && Matcher::vector_element_basic_type(n->in(2)) == T_LONG); + match(Set dst (UMaxReductionV isrc vsrc)); + effect(TEMP_DEF dst, KILL cr); + format %{ "reduce_umaxL_neon $dst, $isrc, $vsrc\t# 2L. KILL cr" %} + ins_encode %{ + __ neon_reduce_minmax_integral(this->ideal_Opcode(), $dst$$Register, T_LONG, + $isrc$$Register, $vsrc$$FloatRegister, + /* vector_length_in_bytes */ 16, fnoreg); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_umaxL_sve(iRegLNoSp dst, iRegL isrc, vReg vsrc, + vRegD tmp, rFlagsReg cr) %{ + predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n->in(2)) == T_LONG); + match(Set dst (UMaxReductionV isrc vsrc)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_umaxL_sve $dst, $isrc, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + uint length_in_bytes = Matcher::vector_length_in_bytes(this, $vsrc); + assert(length_in_bytes == MaxVectorSize, "invalid vector length"); + __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, T_LONG, + $isrc$$Register, $vsrc$$FloatRegister, + ptrue, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +// reduction umax - predicated + +instruct reduce_umaxI_masked(iRegINoSp dst, iRegIorL2I isrc, vReg vsrc, pRegGov pg, + vRegD tmp, rFlagsReg cr) %{ + predicate(UseSVE > 0 && + (Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_BYTE || + Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_SHORT || + Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_INT)); + match(Set dst (UMaxReductionV (Binary isrc vsrc) pg)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_umaxI_masked $dst, $isrc, $pg, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt, + $isrc$$Register, $vsrc$$FloatRegister, + $pg$$PRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + +instruct reduce_umaxL_masked(iRegLNoSp dst, iRegL isrc, vReg vsrc, pRegGov pg, + vRegD tmp, rFlagsReg cr) %{ + predicate(UseSVE > 0 && Matcher::vector_element_basic_type(n->in(1)->in(2)) == T_LONG); + match(Set dst (UMaxReductionV (Binary isrc vsrc) pg)); + effect(TEMP_DEF dst, TEMP tmp, KILL cr); + format %{ "reduce_umaxL_masked $dst, $isrc, $pg, $vsrc\t# KILL $tmp, cr" %} + ins_encode %{ + BasicType bt = Matcher::vector_element_basic_type(this, $vsrc); + __ sve_reduce_integral(this->ideal_Opcode(), $dst$$Register, bt, + $isrc$$Register, $vsrc$$FloatRegister, + $pg$$PRegister, $tmp$$FloatRegister); + %} + ins_pipe(pipe_slow); +%} + // ------------------------------ Vector reinterpret --------------------------- instruct reinterpret_same_size(vReg dst_src) %{ diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index 66dc22c3758..48bffb3cf35 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -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, 2025, Arm Limited. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // @@ -191,6 +191,8 @@ source %{ case Op_XorReductionV: case Op_MinReductionV: case Op_MaxReductionV: + case Op_UMinReductionV: + case Op_UMaxReductionV: // Reductions with less than 8 bytes vector length are // not supported. if (length_in_bytes < 8) { @@ -373,6 +375,8 @@ source %{ return !VM_Version::use_neon_for_vector(length_in_bytes); case Op_MinReductionV: case Op_MaxReductionV: + case Op_UMinReductionV: + case Op_UMaxReductionV: // For BYTE/SHORT/INT/FLOAT/DOUBLE types, we prefer using NEON // instructions rather than SVE predicated instructions for // better performance. @@ -2505,6 +2509,32 @@ REDUCE_MAXMIN_INT_PREDICATE(min, L, iRegL, MinReductionV) REDUCE_MAXMIN_FP_PREDICATE(min, F, fsrc, MinReductionV, sve_fminv, fmins) REDUCE_MAXMIN_FP_PREDICATE(min, D, dsrc, MinReductionV, sve_fminv, fmind) +// -------------------- Vector reduction unsigned min/max ---------------------- + +// reduction uminI +REDUCE_MAXMIN_I_NEON(umin, UMinReductionV) +REDUCE_MAXMIN_I_SVE(umin, UMinReductionV) + +// reduction uminL +REDUCE_MAXMIN_L_NEON(umin, UMinReductionV) +REDUCE_MAXMIN_L_SVE(umin, UMinReductionV) + +// reduction umin - predicated +REDUCE_MAXMIN_INT_PREDICATE(umin, I, iRegIorL2I, UMinReductionV) +REDUCE_MAXMIN_INT_PREDICATE(umin, L, iRegL, UMinReductionV) + +// reduction umaxI +REDUCE_MAXMIN_I_NEON(umax, UMaxReductionV) +REDUCE_MAXMIN_I_SVE(umax, UMaxReductionV) + +// reduction umaxL +REDUCE_MAXMIN_L_NEON(umax, UMaxReductionV) +REDUCE_MAXMIN_L_SVE(umax, UMaxReductionV) + +// reduction umax - predicated +REDUCE_MAXMIN_INT_PREDICATE(umax, I, iRegIorL2I, UMaxReductionV) +REDUCE_MAXMIN_INT_PREDICATE(umax, L, iRegL, UMaxReductionV) + // ------------------------------ Vector reinterpret --------------------------- instruct reinterpret_same_size(vReg dst_src) %{ diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index 18807c667e3..fc6e58b801c 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_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, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2658,6 +2658,8 @@ template INSN(uminv, 1, 0b011011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(smaxp, 0, 0b101001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(sminp, 0, 0b101011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S + INSN(umaxp, 1, 0b101001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S + INSN(uminp, 1, 0b101011, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S INSN(sqdmulh,0, 0b101101, false); // accepted arrangements: T4H, T8H, T2S, T4S INSN(shsubv, 0, 0b001001, false); // accepted arrangements: T8B, T16B, T4H, T8H, T2S, T4S @@ -3490,7 +3492,9 @@ public: INSN(sve_sub, 0b00000100, 0b000001000); // vector sub INSN(sve_uaddv, 0b00000100, 0b000001001); // unsigned add reduction to scalar INSN(sve_umax, 0b00000100, 0b001001000); // unsigned maximum vectors + INSN(sve_umaxv, 0b00000100, 0b001001001); // unsigned maximum reduction to scalar INSN(sve_umin, 0b00000100, 0b001011000); // unsigned minimum vectors + INSN(sve_uminv, 0b00000100, 0b001011001); // unsigned minimum reduction to scalar #undef INSN // SVE floating-point arithmetic - predicate diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index 75897a16fe4..958855c7685 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.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 @@ -1960,50 +1960,76 @@ void C2_MacroAssembler::neon_reduce_logical(int opc, Register dst, BasicType bt, BLOCK_COMMENT("} neon_reduce_logical"); } -// Vector reduction min/max for integral type with ASIMD instructions. +// Helper function to decode min/max reduction operation properties +void C2_MacroAssembler::decode_minmax_reduction_opc(int opc, bool* is_min, + bool* is_unsigned, + Condition* cond) { + switch(opc) { + case Op_MinReductionV: + *is_min = true; *is_unsigned = false; *cond = LT; break; + case Op_MaxReductionV: + *is_min = false; *is_unsigned = false; *cond = GT; break; + case Op_UMinReductionV: + *is_min = true; *is_unsigned = true; *cond = LO; break; + case Op_UMaxReductionV: + *is_min = false; *is_unsigned = true; *cond = HI; break; + default: + ShouldNotReachHere(); + } +} + +// Vector reduction min/max/umin/umax for integral type with ASIMD instructions. // Note: vtmp is not used and expected to be fnoreg for T_LONG case. // Clobbers: rscratch1, rflags void C2_MacroAssembler::neon_reduce_minmax_integral(int opc, Register dst, BasicType bt, Register isrc, FloatRegister vsrc, unsigned vector_length_in_bytes, FloatRegister vtmp) { - assert(opc == Op_MinReductionV || opc == Op_MaxReductionV, "unsupported"); + assert(opc == Op_MinReductionV || opc == Op_MaxReductionV || + opc == Op_UMinReductionV || opc == Op_UMaxReductionV, "unsupported"); assert(vector_length_in_bytes == 8 || vector_length_in_bytes == 16, "unsupported"); assert(bt == T_BYTE || bt == T_SHORT || bt == T_INT || bt == T_LONG, "unsupported"); assert_different_registers(dst, isrc); bool isQ = vector_length_in_bytes == 16; - bool is_min = opc == Op_MinReductionV; - + bool is_min; + bool is_unsigned; + Condition cond; + decode_minmax_reduction_opc(opc, &is_min, &is_unsigned, &cond); BLOCK_COMMENT("neon_reduce_minmax_integral {"); if (bt == T_LONG) { assert(vtmp == fnoreg, "should be"); assert(isQ, "should be"); umov(rscratch1, vsrc, D, 0); cmp(isrc, rscratch1); - csel(dst, isrc, rscratch1, is_min ? LT : GT); + csel(dst, isrc, rscratch1, cond); umov(rscratch1, vsrc, D, 1); cmp(dst, rscratch1); - csel(dst, dst, rscratch1, is_min ? LT : GT); + csel(dst, dst, rscratch1, cond); } else { SIMD_Arrangement size = esize2arrangement((unsigned)type2aelembytes(bt), isQ); if (size == T2S) { - is_min ? sminp(vtmp, size, vsrc, vsrc) : smaxp(vtmp, size, vsrc, vsrc); + // For T2S (2x32-bit elements), use pairwise instructions because + // uminv/umaxv/sminv/smaxv don't support arrangement 2S. + neon_minmaxp(is_unsigned, is_min, vtmp, size, vsrc, vsrc); } else { - is_min ? sminv(vtmp, size, vsrc) : smaxv(vtmp, size, vsrc); + // For other sizes, use reduction to scalar instructions. + neon_minmaxv(is_unsigned, is_min, vtmp, size, vsrc); } if (bt == T_INT) { umov(dst, vtmp, S, 0); + } else if (is_unsigned) { + umov(dst, vtmp, elemType_to_regVariant(bt), 0); } else { smov(dst, vtmp, elemType_to_regVariant(bt), 0); } cmpw(dst, isrc); - cselw(dst, dst, isrc, is_min ? LT : GT); + cselw(dst, dst, isrc, cond); } BLOCK_COMMENT("} neon_reduce_minmax_integral"); } // Vector reduction for integral type with SVE instruction. -// Supported operations are Add, And, Or, Xor, Max, Min. +// Supported operations are Add, And, Or, Xor, Max, Min, UMax, UMin. // rflags would be clobbered if opc is Op_MaxReductionV or Op_MinReductionV. void C2_MacroAssembler::sve_reduce_integral(int opc, Register dst, BasicType bt, Register src1, FloatRegister src2, PRegister pg, FloatRegister tmp) { @@ -2075,35 +2101,27 @@ void C2_MacroAssembler::sve_reduce_integral(int opc, Register dst, BasicType bt, } break; } - case Op_MaxReductionV: { - sve_smaxv(tmp, size, pg, src2); - if (bt == T_INT || bt == T_LONG) { + case Op_MaxReductionV: + case Op_MinReductionV: + case Op_UMaxReductionV: + case Op_UMinReductionV: { + bool is_min; + bool is_unsigned; + Condition cond; + decode_minmax_reduction_opc(opc, &is_min, &is_unsigned, &cond); + sve_minmaxv(is_unsigned, is_min, tmp, size, pg, src2); + // Move result from vector to general register + if (is_unsigned || bt == T_INT || bt == T_LONG) { umov(dst, tmp, size, 0); } else { smov(dst, tmp, size, 0); } if (bt == T_LONG) { cmp(dst, src1); - csel(dst, dst, src1, Assembler::GT); + csel(dst, dst, src1, cond); } else { cmpw(dst, src1); - cselw(dst, dst, src1, Assembler::GT); - } - break; - } - case Op_MinReductionV: { - sve_sminv(tmp, size, pg, src2); - if (bt == T_INT || bt == T_LONG) { - umov(dst, tmp, size, 0); - } else { - smov(dst, tmp, size, 0); - } - if (bt == T_LONG) { - cmp(dst, src1); - csel(dst, dst, src1, Assembler::LT); - } else { - cmpw(dst, src1); - cselw(dst, dst, src1, Assembler::LT); + cselw(dst, dst, src1, cond); } break; } diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp index 412f0f37e9e..4f3a41da402 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.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 @@ -34,6 +34,37 @@ void neon_reduce_logical_helper(int opc, bool sf, Register Rd, Register Rn, Register Rm, enum shift_kind kind = Assembler::LSL, unsigned shift = 0); + // Helper functions for min/max reduction operations + + void decode_minmax_reduction_opc(int opc, bool* is_min, bool* is_unsigned, Condition* cond); + + void neon_minmaxp(bool is_unsigned, bool is_min, FloatRegister dst, + SIMD_Arrangement size, FloatRegister src1, FloatRegister src2) { + auto m = is_unsigned ? (is_min ? &Assembler::uminp : &Assembler::umaxp) + : (is_min ? &Assembler::sminp : &Assembler::smaxp); + (this->*m)(dst, size, src1, src2); + } + + // Typedefs used to disambiguate overloaded member functions. + typedef void (Assembler::*neon_reduction2) + (FloatRegister, SIMD_Arrangement, FloatRegister); + + void neon_minmaxv(bool is_unsigned, bool is_min, FloatRegister dst, + SIMD_Arrangement size, FloatRegister src) { + auto m = is_unsigned ? (is_min ? (neon_reduction2)&Assembler::uminv + : (neon_reduction2)&Assembler::umaxv) + : (is_min ? &Assembler::sminv + : &Assembler::smaxv); + (this->*m)(dst, size, src); + } + + void sve_minmaxv(bool is_unsigned, bool is_min, FloatRegister dst, + SIMD_RegVariant size, PRegister pg, FloatRegister src) { + auto m = is_unsigned ? (is_min ? &Assembler::sve_uminv : &Assembler::sve_umaxv) + : (is_min ? &Assembler::sve_sminv : &Assembler::sve_smaxv); + (this->*m)(dst, size, pg, src); + } + void select_from_two_vectors_neon(FloatRegister dst, FloatRegister src1, FloatRegister src2, FloatRegister index, FloatRegister tmp, unsigned vector_length_in_bytes); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index d56abaa17dd..5e75511b657 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.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 @@ -462,6 +462,8 @@ macro(UMinV) macro(UMaxV) macro(MinReductionV) macro(MaxReductionV) +macro(UMinReductionV) +macro(UMaxReductionV) macro(CompressV) macro(CompressM) macro(ExpandV) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 77b7636a150..80bb1dcf408 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3812,6 +3812,8 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f case Op_MulReductionVD: case Op_MinReductionV: case Op_MaxReductionV: + case Op_UMinReductionV: + case Op_UMaxReductionV: case Op_AndReductionV: case Op_OrReductionV: case Op_XorReductionV: diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 7401efaf7c2..ba88ae9496f 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -1326,6 +1326,32 @@ int ReductionNode::opcode(int opc, BasicType bt) { assert(bt == T_DOUBLE, "must be"); vopc = Op_MaxReductionV; break; + case Op_UMinV: + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + case T_LONG: + vopc = Op_UMinReductionV; + break; + default: ShouldNotReachHere(); return 0; + } + break; + case Op_UMaxV: + switch (bt) { + case T_BOOLEAN: + case T_CHAR: return 0; + case T_BYTE: + case T_SHORT: + case T_INT: + case T_LONG: + vopc = Op_UMaxReductionV; + break; + default: ShouldNotReachHere(); return 0; + } + break; case Op_AndI: switch (bt) { case T_BOOLEAN: @@ -1400,6 +1426,8 @@ ReductionNode* ReductionNode::make(int opc, Node* ctrl, Node* n1, Node* n2, Basi case Op_MulReductionVD: return new MulReductionVDNode(ctrl, n1, n2, requires_strict_order); case Op_MinReductionV: return new MinReductionVNode (ctrl, n1, n2); case Op_MaxReductionV: return new MaxReductionVNode (ctrl, n1, n2); + case Op_UMinReductionV: return new UMinReductionVNode(ctrl, n1, n2); + case Op_UMaxReductionV: return new UMaxReductionVNode(ctrl, n1, n2); case Op_AndReductionV: return new AndReductionVNode (ctrl, n1, n2); case Op_OrReductionV: return new OrReductionVNode (ctrl, n1, n2); case Op_XorReductionV: return new XorReductionVNode (ctrl, n1, n2); @@ -1611,6 +1639,30 @@ Node* ReductionNode::make_identity_con_scalar(PhaseGVN& gvn, int sopc, BasicType default: Unimplemented(); return nullptr; } break; + case Op_UMinReductionV: + switch (bt) { + case T_BYTE: + return gvn.makecon(TypeInt::make(max_jubyte)); + case T_SHORT: + return gvn.makecon(TypeInt::make(max_jushort)); + case T_INT: + return gvn.makecon(TypeInt::MINUS_1); + case T_LONG: + return gvn.makecon(TypeLong::MINUS_1); + default: Unimplemented(); return nullptr; + } + break; + case Op_UMaxReductionV: + switch (bt) { + case T_BYTE: + case T_SHORT: + case T_INT: + return gvn.makecon(TypeInt::ZERO); + case T_LONG: + return gvn.makecon(TypeLong::ZERO); + default: Unimplemented(); return nullptr; + } + break; default: fatal("Missed vector creation for '%s'", NodeClassNames[vopc]); return nullptr; diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp index b1976d8f0db..edbceb30635 100644 --- a/src/hotspot/share/opto/vectornode.hpp +++ b/src/hotspot/share/opto/vectornode.hpp @@ -1015,6 +1015,20 @@ public: virtual int Opcode() const; }; +// Vector unsigned min byte, short, int, long as a reduction +class UMinReductionVNode : public ReductionNode { +public: + UMinReductionVNode(Node* ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} + virtual int Opcode() const; +}; + +// Vector unsigned max byte, short, int, long as a reduction +class UMaxReductionVNode : public ReductionNode { +public: + UMaxReductionVNode(Node* ctrl, Node* in1, Node* in2) : ReductionNode(ctrl, in1, in2) {} + virtual int Opcode() const; +}; + // Vector compress class CompressVNode: public VectorNode { public: diff --git a/test/hotspot/gtest/aarch64/aarch64-asmtest.py b/test/hotspot/gtest/aarch64/aarch64-asmtest.py index 48b19acaa05..1ac2e1a89cd 100644 --- a/test/hotspot/gtest/aarch64/aarch64-asmtest.py +++ b/test/hotspot/gtest/aarch64/aarch64-asmtest.py @@ -1871,6 +1871,12 @@ generate(ThreeRegNEONOp, ["sminp", "sminp", "8B"], ["sminp", "sminp", "16B"], ["sminp", "sminp", "4H"], ["sminp", "sminp", "8H"], ["sminp", "sminp", "2S"], ["sminp", "sminp", "4S"], + ["uminp", "uminp", "8B"], ["uminp", "uminp", "16B"], + ["uminp", "uminp", "4H"], ["uminp", "uminp", "8H"], + ["uminp", "uminp", "2S"], ["uminp", "uminp", "4S"], + ["umaxp", "umaxp", "8B"], ["umaxp", "umaxp", "16B"], + ["umaxp", "umaxp", "4H"], ["umaxp", "umaxp", "8H"], + ["umaxp", "umaxp", "2S"], ["umaxp", "umaxp", "4S"], ["sqdmulh", "sqdmulh", "4H"], ["sqdmulh", "sqdmulh", "8H"], ["sqdmulh", "sqdmulh", "2S"], ["sqdmulh", "sqdmulh", "4S"], ["shsubv", "shsub", "8B"], ["shsubv", "shsub", "16B"], @@ -2252,7 +2258,7 @@ generate(SVEVectorOp, [["add", "ZZZ"], ["uqsub", "ZPZ", "m", "dn"], ]) -generate(SVEReductionOp, [["andv", 0], ["orv", 0], ["eorv", 0], ["smaxv", 0], ["sminv", 0], +generate(SVEReductionOp, [["andv", 0], ["orv", 0], ["eorv", 0], ["smaxv", 0], ["sminv", 0], ["umaxv", 0], ["uminv", 0], ["fminv", 2], ["fmaxv", 2], ["fadda", 2], ["uaddv", 0]]) generate(AddWideNEONOp, diff --git a/test/hotspot/gtest/aarch64/asmtest.out.h b/test/hotspot/gtest/aarch64/asmtest.out.h index 34a5f8ca68e..22bb6c57784 100644 --- a/test/hotspot/gtest/aarch64/asmtest.out.h +++ b/test/hotspot/gtest/aarch64/asmtest.out.h @@ -826,107 +826,119 @@ __ sminp(v10, __ T8H, v11, v12); // sminp v10.8H, v11.8H, v12.8H __ sminp(v20, __ T2S, v21, v22); // sminp v20.2S, v21.2S, v22.2S __ sminp(v10, __ T4S, v11, v12); // sminp v10.4S, v11.4S, v12.4S - __ sqdmulh(v4, __ T4H, v5, v6); // sqdmulh v4.4H, v5.4H, v6.4H - __ sqdmulh(v24, __ T8H, v25, v26); // sqdmulh v24.8H, v25.8H, v26.8H - __ sqdmulh(v17, __ T2S, v18, v19); // sqdmulh v17.2S, v18.2S, v19.2S - __ sqdmulh(v17, __ T4S, v18, v19); // sqdmulh v17.4S, v18.4S, v19.4S - __ shsubv(v22, __ T8B, v23, v24); // shsub v22.8B, v23.8B, v24.8B - __ shsubv(v3, __ T16B, v4, v5); // shsub v3.16B, v4.16B, v5.16B - __ shsubv(v29, __ T4H, v30, v31); // shsub v29.4H, v30.4H, v31.4H - __ shsubv(v15, __ T8H, v16, v17); // shsub v15.8H, v16.8H, v17.8H - __ shsubv(v22, __ T2S, v23, v24); // shsub v22.2S, v23.2S, v24.2S - __ shsubv(v19, __ T4S, v20, v21); // shsub v19.4S, v20.4S, v21.4S + __ uminp(v4, __ T8B, v5, v6); // uminp v4.8B, v5.8B, v6.8B + __ uminp(v24, __ T16B, v25, v26); // uminp v24.16B, v25.16B, v26.16B + __ uminp(v17, __ T4H, v18, v19); // uminp v17.4H, v18.4H, v19.4H + __ uminp(v17, __ T8H, v18, v19); // uminp v17.8H, v18.8H, v19.8H + __ uminp(v22, __ T2S, v23, v24); // uminp v22.2S, v23.2S, v24.2S + __ uminp(v3, __ T4S, v4, v5); // uminp v3.4S, v4.4S, v5.4S + __ umaxp(v29, __ T8B, v30, v31); // umaxp v29.8B, v30.8B, v31.8B + __ umaxp(v15, __ T16B, v16, v17); // umaxp v15.16B, v16.16B, v17.16B + __ umaxp(v22, __ T4H, v23, v24); // umaxp v22.4H, v23.4H, v24.4H + __ umaxp(v19, __ T8H, v20, v21); // umaxp v19.8H, v20.8H, v21.8H + __ umaxp(v19, __ T2S, v20, v21); // umaxp v19.2S, v20.2S, v21.2S + __ umaxp(v22, __ T4S, v23, v24); // umaxp v22.4S, v23.4S, v24.4S + __ sqdmulh(v2, __ T4H, v3, v4); // sqdmulh v2.4H, v3.4H, v4.4H + __ sqdmulh(v15, __ T8H, v16, v17); // sqdmulh v15.8H, v16.8H, v17.8H + __ sqdmulh(v6, __ T2S, v7, v8); // sqdmulh v6.2S, v7.2S, v8.2S + __ sqdmulh(v12, __ T4S, v13, v14); // sqdmulh v12.4S, v13.4S, v14.4S + __ shsubv(v16, __ T8B, v17, v18); // shsub v16.8B, v17.8B, v18.8B + __ shsubv(v11, __ T16B, v12, v13); // shsub v11.16B, v12.16B, v13.16B + __ shsubv(v13, __ T4H, v14, v15); // shsub v13.4H, v14.4H, v15.4H + __ shsubv(v23, __ T8H, v24, v25); // shsub v23.8H, v24.8H, v25.8H + __ shsubv(v1, __ T2S, v2, v3); // shsub v1.2S, v2.2S, v3.2S + __ shsubv(v30, __ T4S, v31, v0); // shsub v30.4S, v31.4S, v0.4S __ fmin(v19, __ T2S, v20, v21); // fmin v19.2S, v20.2S, v21.2S - __ fmin(v22, __ T4S, v23, v24); // fmin v22.4S, v23.4S, v24.4S - __ fmin(v2, __ T2D, v3, v4); // fmin v2.2D, v3.2D, v4.2D - __ fmin(v15, __ T4H, v16, v17); // fmin v15.4H, v16.4H, v17.4H - __ fmin(v6, __ T8H, v7, v8); // fmin v6.8H, v7.8H, v8.8H - __ facgt(v12, __ T2S, v13, v14); // facgt v12.2S, v13.2S, v14.2S - __ facgt(v16, __ T4S, v17, v18); // facgt v16.4S, v17.4S, v18.4S - __ facgt(v11, __ T2D, v12, v13); // facgt v11.2D, v12.2D, v13.2D - __ facgt(v13, __ T4H, v14, v15); // facgt v13.4H, v14.4H, v15.4H - __ facgt(v23, __ T8H, v24, v25); // facgt v23.8H, v24.8H, v25.8H + __ fmin(v5, __ T4S, v6, v7); // fmin v5.4S, v6.4S, v7.4S + __ fmin(v17, __ T2D, v18, v19); // fmin v17.2D, v18.2D, v19.2D + __ fmin(v2, __ T4H, v3, v4); // fmin v2.4H, v3.4H, v4.4H + __ fmin(v16, __ T8H, v17, v18); // fmin v16.8H, v17.8H, v18.8H + __ facgt(v22, __ T2S, v23, v24); // facgt v22.2S, v23.2S, v24.2S + __ facgt(v13, __ T4S, v14, v15); // facgt v13.4S, v14.4S, v15.4S + __ facgt(v10, __ T2D, v11, v12); // facgt v10.2D, v11.2D, v12.2D + __ facgt(v21, __ T4H, v22, v23); // facgt v21.4H, v22.4H, v23.4H + __ facgt(v29, __ T8H, v30, v31); // facgt v29.8H, v30.8H, v31.8H // VectorScalarNEONInstruction - __ fmlavs(v15, __ T2S, v0, v1, 0); // fmla v15.2S, v0.2S, v1.S[0] - __ mulvs(v2, __ T4S, v3, v4, 2); // mul v2.4S, v3.4S, v4.S[2] - __ fmlavs(v1, __ T2D, v2, v3, 1); // fmla v1.2D, v2.2D, v3.D[1] - __ fmlsvs(v11, __ T2S, v12, v13, 1); // fmls v11.2S, v12.2S, v13.S[1] - __ mulvs(v5, __ T4S, v6, v7, 1); // mul v5.4S, v6.4S, v7.S[1] - __ fmlsvs(v14, __ T2D, v15, v16, 1); // fmls v14.2D, v15.2D, v16.D[1] - __ fmulxvs(v6, __ T2S, v7, v8, 1); // fmulx v6.2S, v7.2S, v8.S[1] + __ fmlavs(v6, __ T2S, v7, v8, 1); // fmla v6.2S, v7.2S, v8.S[1] __ mulvs(v1, __ T4S, v2, v3, 3); // mul v1.4S, v2.4S, v3.S[3] - __ fmulxvs(v15, __ T2D, v0, v1, 0); // fmulx v15.2D, v0.2D, v1.D[0] - __ mulvs(v9, __ T4H, v10, v11, 3); // mul v9.4H, v10.4H, v11.H[3] - __ mulvs(v4, __ T8H, v5, v6, 4); // mul v4.8H, v5.8H, v6.H[4] - __ mulvs(v13, __ T2S, v14, v15, 1); // mul v13.2S, v14.2S, v15.S[1] - __ mulvs(v3, __ T4S, v4, v5, 1); // mul v3.4S, v4.4S, v5.S[1] + __ fmlavs(v15, __ T2D, v0, v1, 0); // fmla v15.2D, v0.2D, v1.D[0] + __ fmlsvs(v9, __ T2S, v10, v11, 1); // fmls v9.2S, v10.2S, v11.S[1] + __ mulvs(v4, __ T4S, v5, v6, 2); // mul v4.4S, v5.4S, v6.S[2] + __ fmlsvs(v13, __ T2D, v14, v15, 1); // fmls v13.2D, v14.2D, v15.D[1] + __ fmulxvs(v3, __ T2S, v4, v5, 0); // fmulx v3.2S, v4.2S, v5.S[0] + __ mulvs(v11, __ T4S, v12, v13, 2); // mul v11.4S, v12.4S, v13.S[2] + __ fmulxvs(v12, __ T2D, v13, v14, 1); // fmulx v12.2D, v13.2D, v14.D[1] + __ mulvs(v15, __ T4H, v0, v1, 0); // mul v15.4H, v0.4H, v1.H[0] + __ mulvs(v9, __ T8H, v10, v11, 6); // mul v9.8H, v10.8H, v11.H[6] + __ mulvs(v11, __ T2S, v12, v13, 0); // mul v11.2S, v12.2S, v13.S[0] + __ mulvs(v1, __ T4S, v2, v3, 2); // mul v1.4S, v2.4S, v3.S[2] // NEONVectorCompare - __ cm(Assembler::GT, v21, __ T8B, v22, v23); // cmgt v21.8B, v22.8B, v23.8B - __ cm(Assembler::GT, v23, __ T16B, v24, v25); // cmgt v23.16B, v24.16B, v25.16B - __ cm(Assembler::GT, v31, __ T4H, v0, v1); // cmgt v31.4H, v0.4H, v1.4H - __ cm(Assembler::GT, v25, __ T8H, v26, v27); // cmgt v25.8H, v26.8H, v27.8H - __ cm(Assembler::GT, v2, __ T2S, v3, v4); // cmgt v2.2S, v3.2S, v4.2S - __ cm(Assembler::GT, v31, __ T4S, v0, v1); // cmgt v31.4S, v0.4S, v1.4S - __ cm(Assembler::GT, v27, __ T2D, v28, v29); // cmgt v27.2D, v28.2D, v29.2D - __ cm(Assembler::GE, v18, __ T8B, v19, v20); // cmge v18.8B, v19.8B, v20.8B - __ cm(Assembler::GE, v10, __ T16B, v11, v12); // cmge v10.16B, v11.16B, v12.16B - __ cm(Assembler::GE, v23, __ T4H, v24, v25); // cmge v23.4H, v24.4H, v25.4H - __ cm(Assembler::GE, v19, __ T8H, v20, v21); // cmge v19.8H, v20.8H, v21.8H - __ cm(Assembler::GE, v3, __ T2S, v4, v5); // cmge v3.2S, v4.2S, v5.2S - __ cm(Assembler::GE, v18, __ T4S, v19, v20); // cmge v18.4S, v19.4S, v20.4S - __ cm(Assembler::GE, v0, __ T2D, v1, v2); // cmge v0.2D, v1.2D, v2.2D - __ cm(Assembler::EQ, v25, __ T8B, v26, v27); // cmeq v25.8B, v26.8B, v27.8B - __ cm(Assembler::EQ, v26, __ T16B, v27, v28); // cmeq v26.16B, v27.16B, v28.16B - __ cm(Assembler::EQ, v23, __ T4H, v24, v25); // cmeq v23.4H, v24.4H, v25.4H - __ cm(Assembler::EQ, v2, __ T8H, v3, v4); // cmeq v2.8H, v3.8H, v4.8H - __ cm(Assembler::EQ, v18, __ T2S, v19, v20); // cmeq v18.2S, v19.2S, v20.2S - __ cm(Assembler::EQ, v12, __ T4S, v13, v14); // cmeq v12.4S, v13.4S, v14.4S - __ cm(Assembler::EQ, v4, __ T2D, v5, v6); // cmeq v4.2D, v5.2D, v6.2D - __ cm(Assembler::HI, v28, __ T8B, v29, v30); // cmhi v28.8B, v29.8B, v30.8B - __ cm(Assembler::HI, v30, __ T16B, v31, v0); // cmhi v30.16B, v31.16B, v0.16B - __ cm(Assembler::HI, v29, __ T4H, v30, v31); // cmhi v29.4H, v30.4H, v31.4H - __ cm(Assembler::HI, v16, __ T8H, v17, v18); // cmhi v16.8H, v17.8H, v18.8H - __ cm(Assembler::HI, v27, __ T2S, v28, v29); // cmhi v27.2S, v28.2S, v29.2S - __ cm(Assembler::HI, v6, __ T4S, v7, v8); // cmhi v6.4S, v7.4S, v8.4S - __ cm(Assembler::HI, v9, __ T2D, v10, v11); // cmhi v9.2D, v10.2D, v11.2D - __ cm(Assembler::HS, v29, __ T8B, v30, v31); // cmhs v29.8B, v30.8B, v31.8B - __ cm(Assembler::HS, v18, __ T16B, v19, v20); // cmhs v18.16B, v19.16B, v20.16B - __ cm(Assembler::HS, v7, __ T4H, v8, v9); // cmhs v7.4H, v8.4H, v9.4H - __ cm(Assembler::HS, v4, __ T8H, v5, v6); // cmhs v4.8H, v5.8H, v6.8H - __ cm(Assembler::HS, v7, __ T2S, v8, v9); // cmhs v7.2S, v8.2S, v9.2S + __ cm(Assembler::GT, v18, __ T8B, v19, v20); // cmgt v18.8B, v19.8B, v20.8B + __ cm(Assembler::GT, v0, __ T16B, v1, v2); // cmgt v0.16B, v1.16B, v2.16B + __ cm(Assembler::GT, v25, __ T4H, v26, v27); // cmgt v25.4H, v26.4H, v27.4H + __ cm(Assembler::GT, v26, __ T8H, v27, v28); // cmgt v26.8H, v27.8H, v28.8H + __ cm(Assembler::GT, v23, __ T2S, v24, v25); // cmgt v23.2S, v24.2S, v25.2S + __ cm(Assembler::GT, v2, __ T4S, v3, v4); // cmgt v2.4S, v3.4S, v4.4S + __ cm(Assembler::GT, v18, __ T2D, v19, v20); // cmgt v18.2D, v19.2D, v20.2D + __ cm(Assembler::GE, v12, __ T8B, v13, v14); // cmge v12.8B, v13.8B, v14.8B + __ cm(Assembler::GE, v4, __ T16B, v5, v6); // cmge v4.16B, v5.16B, v6.16B + __ cm(Assembler::GE, v28, __ T4H, v29, v30); // cmge v28.4H, v29.4H, v30.4H + __ cm(Assembler::GE, v30, __ T8H, v31, v0); // cmge v30.8H, v31.8H, v0.8H + __ cm(Assembler::GE, v29, __ T2S, v30, v31); // cmge v29.2S, v30.2S, v31.2S + __ cm(Assembler::GE, v16, __ T4S, v17, v18); // cmge v16.4S, v17.4S, v18.4S + __ cm(Assembler::GE, v27, __ T2D, v28, v29); // cmge v27.2D, v28.2D, v29.2D + __ cm(Assembler::EQ, v6, __ T8B, v7, v8); // cmeq v6.8B, v7.8B, v8.8B + __ cm(Assembler::EQ, v9, __ T16B, v10, v11); // cmeq v9.16B, v10.16B, v11.16B + __ cm(Assembler::EQ, v29, __ T4H, v30, v31); // cmeq v29.4H, v30.4H, v31.4H + __ cm(Assembler::EQ, v18, __ T8H, v19, v20); // cmeq v18.8H, v19.8H, v20.8H + __ cm(Assembler::EQ, v7, __ T2S, v8, v9); // cmeq v7.2S, v8.2S, v9.2S + __ cm(Assembler::EQ, v4, __ T4S, v5, v6); // cmeq v4.4S, v5.4S, v6.4S + __ cm(Assembler::EQ, v7, __ T2D, v8, v9); // cmeq v7.2D, v8.2D, v9.2D + __ cm(Assembler::HI, v15, __ T8B, v16, v17); // cmhi v15.8B, v16.8B, v17.8B + __ cm(Assembler::HI, v9, __ T16B, v10, v11); // cmhi v9.16B, v10.16B, v11.16B + __ cm(Assembler::HI, v23, __ T4H, v24, v25); // cmhi v23.4H, v24.4H, v25.4H + __ cm(Assembler::HI, v8, __ T8H, v9, v10); // cmhi v8.8H, v9.8H, v10.8H + __ cm(Assembler::HI, v2, __ T2S, v3, v4); // cmhi v2.2S, v3.2S, v4.2S + __ cm(Assembler::HI, v28, __ T4S, v29, v30); // cmhi v28.4S, v29.4S, v30.4S + __ cm(Assembler::HI, v21, __ T2D, v22, v23); // cmhi v21.2D, v22.2D, v23.2D + __ cm(Assembler::HS, v31, __ T8B, v0, v1); // cmhs v31.8B, v0.8B, v1.8B + __ cm(Assembler::HS, v5, __ T16B, v6, v7); // cmhs v5.16B, v6.16B, v7.16B + __ cm(Assembler::HS, v27, __ T4H, v28, v29); // cmhs v27.4H, v28.4H, v29.4H + __ cm(Assembler::HS, v0, __ T8H, v1, v2); // cmhs v0.8H, v1.8H, v2.8H + __ cm(Assembler::HS, v17, __ T2S, v18, v19); // cmhs v17.2S, v18.2S, v19.2S __ cm(Assembler::HS, v15, __ T4S, v16, v17); // cmhs v15.4S, v16.4S, v17.4S - __ cm(Assembler::HS, v9, __ T2D, v10, v11); // cmhs v9.2D, v10.2D, v11.2D - __ fcm(Assembler::EQ, v23, __ T2S, v24, v25); // fcmeq v23.2S, v24.2S, v25.2S + __ cm(Assembler::HS, v4, __ T2D, v5, v6); // cmhs v4.2D, v5.2D, v6.2D + __ fcm(Assembler::EQ, v26, __ T2S, v27, v28); // fcmeq v26.2S, v27.2S, v28.2S __ fcm(Assembler::EQ, v8, __ T4S, v9, v10); // fcmeq v8.4S, v9.4S, v10.4S - __ fcm(Assembler::EQ, v2, __ T2D, v3, v4); // fcmeq v2.2D, v3.2D, v4.2D - __ fcm(Assembler::GT, v28, __ T2S, v29, v30); // fcmgt v28.2S, v29.2S, v30.2S - __ fcm(Assembler::GT, v21, __ T4S, v22, v23); // fcmgt v21.4S, v22.4S, v23.4S - __ fcm(Assembler::GT, v31, __ T2D, v0, v1); // fcmgt v31.2D, v0.2D, v1.2D - __ fcm(Assembler::GE, v5, __ T2S, v6, v7); // fcmge v5.2S, v6.2S, v7.2S - __ fcm(Assembler::GE, v27, __ T4S, v28, v29); // fcmge v27.4S, v28.4S, v29.4S + __ fcm(Assembler::EQ, v28, __ T2D, v29, v30); // fcmeq v28.2D, v29.2D, v30.2D + __ fcm(Assembler::GT, v22, __ T2S, v23, v24); // fcmgt v22.2S, v23.2S, v24.2S + __ fcm(Assembler::GT, v27, __ T4S, v28, v29); // fcmgt v27.4S, v28.4S, v29.4S + __ fcm(Assembler::GT, v27, __ T2D, v28, v29); // fcmgt v27.2D, v28.2D, v29.2D + __ fcm(Assembler::GE, v25, __ T2S, v26, v27); // fcmge v25.2S, v26.2S, v27.2S + __ fcm(Assembler::GE, v23, __ T4S, v24, v25); // fcmge v23.4S, v24.4S, v25.4S __ fcm(Assembler::GE, v0, __ T2D, v1, v2); // fcmge v0.2D, v1.2D, v2.2D // SVEComparisonWithZero - __ sve_fcm(Assembler::EQ, p8, __ S, p6, z15, 0.0); // fcmeq p8.s, p6/z, z15.s, #0.0 - __ sve_fcm(Assembler::GT, p4, __ D, p6, z28, 0.0); // fcmgt p4.d, p6/z, z28.d, #0.0 - __ sve_fcm(Assembler::GE, p13, __ D, p0, z25, 0.0); // fcmge p13.d, p0/z, z25.d, #0.0 - __ sve_fcm(Assembler::LT, p2, __ D, p0, z6, 0.0); // fcmlt p2.d, p0/z, z6.d, #0.0 - __ sve_fcm(Assembler::LE, p2, __ S, p2, z15, 0.0); // fcmle p2.s, p2/z, z15.s, #0.0 - __ sve_fcm(Assembler::NE, p3, __ S, p7, z5, 0.0); // fcmne p3.s, p7/z, z5.s, #0.0 + __ sve_fcm(Assembler::EQ, p2, __ D, p0, z6, 0.0); // fcmeq p2.d, p0/z, z6.d, #0.0 + __ sve_fcm(Assembler::GT, p2, __ S, p2, z15, 0.0); // fcmgt p2.s, p2/z, z15.s, #0.0 + __ sve_fcm(Assembler::GE, p3, __ S, p7, z5, 0.0); // fcmge p3.s, p7/z, z5.s, #0.0 + __ sve_fcm(Assembler::LT, p3, __ D, p5, z20, 0.0); // fcmlt p3.d, p5/z, z20.d, #0.0 + __ sve_fcm(Assembler::LE, p3, __ S, p4, z11, 0.0); // fcmle p3.s, p4/z, z11.s, #0.0 + __ sve_fcm(Assembler::NE, p15, __ D, p0, z6, 0.0); // fcmne p15.d, p0/z, z6.d, #0.0 // SVEComparisonWithImm - __ sve_cmp(Assembler::EQ, p3, __ S, p5, z20, -10); // cmpeq p3.s, p5/z, z20.s, #-10 - __ sve_cmp(Assembler::GT, p5, __ S, p7, z8, -10); // cmpgt p5.s, p7/z, z8.s, #-10 - __ sve_cmp(Assembler::GE, p8, __ H, p7, z2, 13); // cmpge p8.h, p7/z, z2.h, #13 - __ sve_cmp(Assembler::LT, p1, __ S, p7, z27, -2); // cmplt p1.s, p7/z, z27.s, #-2 - __ sve_cmp(Assembler::LE, p6, __ S, p6, z28, -11); // cmple p6.s, p6/z, z28.s, #-11 - __ sve_cmp(Assembler::NE, p1, __ H, p4, z14, -5); // cmpne p1.h, p4/z, z14.h, #-5 - __ sve_cmp(Assembler::HS, p13, __ H, p1, z23, 90); // cmphs p13.h, p1/z, z23.h, #90 - __ sve_cmp(Assembler::HI, p8, __ B, p4, z4, 66); // cmphi p8.b, p4/z, z4.b, #66 - __ sve_cmp(Assembler::LS, p9, __ H, p3, z13, 11); // cmpls p9.h, p3/z, z13.h, #11 - __ sve_cmp(Assembler::LO, p8, __ S, p5, z3, 21); // cmplo p8.s, p5/z, z3.s, #21 + __ sve_cmp(Assembler::EQ, p6, __ D, p0, z30, 11); // cmpeq p6.d, p0/z, z30.d, #11 + __ sve_cmp(Assembler::GT, p11, __ H, p3, z29, 12); // cmpgt p11.h, p3/z, z29.h, #12 + __ sve_cmp(Assembler::GE, p8, __ B, p0, z24, -2); // cmpge p8.b, p0/z, z24.b, #-2 + __ sve_cmp(Assembler::LT, p5, __ H, p6, z16, 7); // cmplt p5.h, p6/z, z16.h, #7 + __ sve_cmp(Assembler::LE, p6, __ S, p4, z4, -12); // cmple p6.s, p4/z, z4.s, #-12 + __ sve_cmp(Assembler::NE, p0, __ S, p4, z19, -3); // cmpne p0.s, p4/z, z19.s, #-3 + __ sve_cmp(Assembler::HS, p7, __ B, p4, z12, 15); // cmphs p7.b, p4/z, z12.b, #15 + __ sve_cmp(Assembler::HI, p10, __ B, p1, z23, 30); // cmphi p10.b, p1/z, z23.b, #30 + __ sve_cmp(Assembler::LS, p9, __ D, p4, z13, 67); // cmpls p9.d, p4/z, z13.d, #67 + __ sve_cmp(Assembler::LO, p3, __ D, p0, z2, 16); // cmplo p3.d, p0/z, z2.d, #16 // SpecialCases __ ccmn(zr, zr, 3u, Assembler::LE); // ccmn xzr, xzr, #3, LE @@ -1198,239 +1210,241 @@ __ fmovd(v0, -1.0625); // fmov d0, #-1.0625 // LSEOp - __ swp(Assembler::xword, r6, r7, r19); // swp x6, x7, [x19] - __ ldadd(Assembler::xword, r13, r28, r17); // ldadd x13, x28, [x17] - __ ldbic(Assembler::xword, r16, r6, r2); // ldclr x16, x6, [x2] - __ ldeor(Assembler::xword, r29, r3, r4); // ldeor x29, x3, [x4] - __ ldorr(Assembler::xword, r6, r16, r20); // ldset x6, x16, [x20] - __ ldsmin(Assembler::xword, r13, r12, r20); // ldsmin x13, x12, [x20] - __ ldsmax(Assembler::xword, r8, r25, r20); // ldsmax x8, x25, [x20] - __ ldumin(Assembler::xword, r19, r0, r11); // ldumin x19, x0, [x11] - __ ldumax(Assembler::xword, r24, r6, r20); // ldumax x24, x6, [x20] + __ swp(Assembler::xword, r6, r16, r20); // swp x6, x16, [x20] + __ ldadd(Assembler::xword, r13, r12, r20); // ldadd x13, x12, [x20] + __ ldbic(Assembler::xword, r8, r25, r20); // ldclr x8, x25, [x20] + __ ldeor(Assembler::xword, r19, r0, r11); // ldeor x19, x0, [x11] + __ ldorr(Assembler::xword, r24, r6, r20); // ldset x24, x6, [x20] + __ ldsmin(Assembler::xword, zr, r14, r16); // ldsmin xzr, x14, [x16] + __ ldsmax(Assembler::xword, r6, r0, r7); // ldsmax x6, x0, [x7] + __ ldumin(Assembler::xword, r15, r19, r26); // ldumin x15, x19, [x26] + __ ldumax(Assembler::xword, r9, r10, r23); // ldumax x9, x10, [x23] // LSEOp - __ swpa(Assembler::xword, zr, r14, r16); // swpa xzr, x14, [x16] - __ ldadda(Assembler::xword, r6, r0, r7); // ldadda x6, x0, [x7] - __ ldbica(Assembler::xword, r15, r19, r26); // ldclra x15, x19, [x26] - __ ldeora(Assembler::xword, r9, r10, r23); // ldeora x9, x10, [x23] - __ ldorra(Assembler::xword, r21, r22, r28); // ldseta x21, x22, [x28] - __ ldsmina(Assembler::xword, r2, r3, r15); // ldsmina x2, x3, [x15] - __ ldsmaxa(Assembler::xword, r19, r20, r7); // ldsmaxa x19, x20, [x7] - __ ldumina(Assembler::xword, r4, r29, r7); // ldumina x4, x29, [x7] - __ ldumaxa(Assembler::xword, r0, r9, r16); // ldumaxa x0, x9, [x16] + __ swpa(Assembler::xword, r21, r22, r28); // swpa x21, x22, [x28] + __ ldadda(Assembler::xword, r2, r3, r15); // ldadda x2, x3, [x15] + __ ldbica(Assembler::xword, r19, r20, r7); // ldclra x19, x20, [x7] + __ ldeora(Assembler::xword, r4, r29, r7); // ldeora x4, x29, [x7] + __ ldorra(Assembler::xword, r0, r9, r16); // ldseta x0, x9, [x16] + __ ldsmina(Assembler::xword, r20, r23, r4); // ldsmina x20, x23, [x4] + __ ldsmaxa(Assembler::xword, r16, r10, r23); // ldsmaxa x16, x10, [x23] + __ ldumina(Assembler::xword, r11, r25, r6); // ldumina x11, x25, [x6] + __ ldumaxa(Assembler::xword, zr, r16, r13); // ldumaxa xzr, x16, [x13] // LSEOp - __ swpal(Assembler::xword, r20, r23, r4); // swpal x20, x23, [x4] - __ ldaddal(Assembler::xword, r16, r10, r23); // ldaddal x16, x10, [x23] - __ ldbical(Assembler::xword, r11, r25, r6); // ldclral x11, x25, [x6] - __ ldeoral(Assembler::xword, zr, r16, r13); // ldeoral xzr, x16, [x13] - __ ldorral(Assembler::xword, r23, r12, r1); // ldsetal x23, x12, [x1] - __ ldsminal(Assembler::xword, r14, r9, r21); // ldsminal x14, x9, [x21] - __ ldsmaxal(Assembler::xword, r16, r26, r15); // ldsmaxal x16, x26, [x15] - __ lduminal(Assembler::xword, r4, r4, r15); // lduminal x4, x4, [x15] - __ ldumaxal(Assembler::xword, r8, r6, r30); // ldumaxal x8, x6, [x30] + __ swpal(Assembler::xword, r23, r12, r1); // swpal x23, x12, [x1] + __ ldaddal(Assembler::xword, r14, r9, r21); // ldaddal x14, x9, [x21] + __ ldbical(Assembler::xword, r16, r26, r15); // ldclral x16, x26, [x15] + __ ldeoral(Assembler::xword, r4, r4, r15); // ldeoral x4, x4, [x15] + __ ldorral(Assembler::xword, r8, r6, r30); // ldsetal x8, x6, [x30] + __ ldsminal(Assembler::xword, r4, r29, r17); // ldsminal x4, x29, [x17] + __ ldsmaxal(Assembler::xword, r29, r26, r9); // ldsmaxal x29, x26, [x9] + __ lduminal(Assembler::xword, r15, r2, r11); // lduminal x15, x2, [x11] + __ ldumaxal(Assembler::xword, r29, r3, r7); // ldumaxal x29, x3, [x7] // LSEOp - __ swpl(Assembler::xword, r4, r29, r17); // swpl x4, x29, [x17] - __ ldaddl(Assembler::xword, r29, r26, r9); // ldaddl x29, x26, [x9] - __ ldbicl(Assembler::xword, r15, r2, r11); // ldclrl x15, x2, [x11] - __ ldeorl(Assembler::xword, r29, r3, r7); // ldeorl x29, x3, [x7] - __ ldorrl(Assembler::xword, r1, r27, r21); // ldsetl x1, x27, [x21] - __ ldsminl(Assembler::xword, r16, r14, r8); // ldsminl x16, x14, [x8] - __ ldsmaxl(Assembler::xword, r16, r22, r25); // ldsmaxl x16, x22, [x25] - __ lduminl(Assembler::xword, r5, r20, r21); // lduminl x5, x20, [x21] - __ ldumaxl(Assembler::xword, r16, r23, r16); // ldumaxl x16, x23, [x16] + __ swpl(Assembler::xword, r1, r27, r21); // swpl x1, x27, [x21] + __ ldaddl(Assembler::xword, r16, r14, r8); // ldaddl x16, x14, [x8] + __ ldbicl(Assembler::xword, r16, r22, r25); // ldclrl x16, x22, [x25] + __ ldeorl(Assembler::xword, r5, r20, r21); // ldeorl x5, x20, [x21] + __ ldorrl(Assembler::xword, r16, r23, r16); // ldsetl x16, x23, [x16] + __ ldsminl(Assembler::xword, r30, r20, r20); // ldsminl x30, x20, [x20] + __ ldsmaxl(Assembler::xword, r0, r4, r19); // ldsmaxl x0, x4, [x19] + __ lduminl(Assembler::xword, r24, r4, r20); // lduminl x24, x4, [x20] + __ ldumaxl(Assembler::xword, r4, r24, r26); // ldumaxl x4, x24, [x26] // LSEOp - __ swp(Assembler::word, r30, r20, r20); // swp w30, w20, [x20] - __ ldadd(Assembler::word, r0, r4, r19); // ldadd w0, w4, [x19] - __ ldbic(Assembler::word, r24, r4, r20); // ldclr w24, w4, [x20] - __ ldeor(Assembler::word, r4, r24, r26); // ldeor w4, w24, [x26] - __ ldorr(Assembler::word, r19, r2, r8); // ldset w19, w2, [x8] - __ ldsmin(Assembler::word, r8, r14, r24); // ldsmin w8, w14, [x24] - __ ldsmax(Assembler::word, r16, zr, r22); // ldsmax w16, wzr, [x22] - __ ldumin(Assembler::word, r4, zr, r1); // ldumin w4, wzr, [x1] - __ ldumax(Assembler::word, r10, r20, r12); // ldumax w10, w20, [x12] + __ swp(Assembler::word, r19, r2, r8); // swp w19, w2, [x8] + __ ldadd(Assembler::word, r8, r14, r24); // ldadd w8, w14, [x24] + __ ldbic(Assembler::word, r16, zr, r22); // ldclr w16, wzr, [x22] + __ ldeor(Assembler::word, r4, zr, r1); // ldeor w4, wzr, [x1] + __ ldorr(Assembler::word, r10, r20, r12); // ldset w10, w20, [x12] + __ ldsmin(Assembler::word, r0, r9, r7); // ldsmin w0, w9, [x7] + __ ldsmax(Assembler::word, r24, r16, r4); // ldsmax w24, w16, [x4] + __ ldumin(Assembler::word, r27, r6, r10); // ldumin w27, w6, [x10] + __ ldumax(Assembler::word, r27, r24, r13); // ldumax w27, w24, [x13] // LSEOp - __ swpa(Assembler::word, r0, r9, r7); // swpa w0, w9, [x7] - __ ldadda(Assembler::word, r24, r16, r4); // ldadda w24, w16, [x4] - __ ldbica(Assembler::word, r27, r6, r10); // ldclra w27, w6, [x10] - __ ldeora(Assembler::word, r27, r24, r13); // ldeora w27, w24, [x13] - __ ldorra(Assembler::word, r16, zr, r22); // ldseta w16, wzr, [x22] - __ ldsmina(Assembler::word, r22, r20, sp); // ldsmina w22, w20, [sp] - __ ldsmaxa(Assembler::word, r29, r9, r14); // ldsmaxa w29, w9, [x14] - __ ldumina(Assembler::word, r20, r7, r20); // ldumina w20, w7, [x20] - __ ldumaxa(Assembler::word, r28, r9, r11); // ldumaxa w28, w9, [x11] + __ swpa(Assembler::word, r16, zr, r22); // swpa w16, wzr, [x22] + __ ldadda(Assembler::word, r22, r20, sp); // ldadda w22, w20, [sp] + __ ldbica(Assembler::word, r29, r9, r14); // ldclra w29, w9, [x14] + __ ldeora(Assembler::word, r20, r7, r20); // ldeora w20, w7, [x20] + __ ldorra(Assembler::word, r28, r9, r11); // ldseta w28, w9, [x11] + __ ldsmina(Assembler::word, r14, r12, r20); // ldsmina w14, w12, [x20] + __ ldsmaxa(Assembler::word, r1, r24, r9); // ldsmaxa w1, w24, [x9] + __ ldumina(Assembler::word, r19, r13, r19); // ldumina w19, w13, [x19] + __ ldumaxa(Assembler::word, r16, r16, r5); // ldumaxa w16, w16, [x5] // LSEOp - __ swpal(Assembler::word, r14, r12, r20); // swpal w14, w12, [x20] - __ ldaddal(Assembler::word, r1, r24, r9); // ldaddal w1, w24, [x9] - __ ldbical(Assembler::word, r19, r13, r19); // ldclral w19, w13, [x19] - __ ldeoral(Assembler::word, r16, r16, r5); // ldeoral w16, w16, [x5] - __ ldorral(Assembler::word, r0, r3, r12); // ldsetal w0, w3, [x12] - __ ldsminal(Assembler::word, r8, r15, r15); // ldsminal w8, w15, [x15] - __ ldsmaxal(Assembler::word, r16, r4, r15); // ldsmaxal w16, w4, [x15] - __ lduminal(Assembler::word, r30, r5, r0); // lduminal w30, w5, [x0] - __ ldumaxal(Assembler::word, r10, r22, r27); // ldumaxal w10, w22, [x27] + __ swpal(Assembler::word, r0, r3, r12); // swpal w0, w3, [x12] + __ ldaddal(Assembler::word, r8, r15, r15); // ldaddal w8, w15, [x15] + __ ldbical(Assembler::word, r16, r4, r15); // ldclral w16, w4, [x15] + __ ldeoral(Assembler::word, r30, r5, r0); // ldeoral w30, w5, [x0] + __ ldorral(Assembler::word, r10, r22, r27); // ldsetal w10, w22, [x27] + __ ldsminal(Assembler::word, r3, r0, r9); // ldsminal w3, w0, [x9] + __ ldsmaxal(Assembler::word, r19, r29, r10); // ldsmaxal w19, w29, [x10] + __ lduminal(Assembler::word, r24, r4, r20); // lduminal w24, w4, [x20] + __ ldumaxal(Assembler::word, r7, r24, r29); // ldumaxal w7, w24, [x29] // LSEOp - __ swpl(Assembler::word, r3, r0, r9); // swpl w3, w0, [x9] - __ ldaddl(Assembler::word, r19, r29, r10); // ldaddl w19, w29, [x10] - __ ldbicl(Assembler::word, r24, r4, r20); // ldclrl w24, w4, [x20] - __ ldeorl(Assembler::word, r7, r24, r29); // ldeorl w7, w24, [x29] - __ ldorrl(Assembler::word, r14, r21, r11); // ldsetl w14, w21, [x11] - __ ldsminl(Assembler::word, r27, r13, r15); // ldsminl w27, w13, [x15] - __ ldsmaxl(Assembler::word, zr, r17, r14); // ldsmaxl wzr, w17, [x14] - __ lduminl(Assembler::word, r3, r30, r16); // lduminl w3, w30, [x16] - __ ldumaxl(Assembler::word, r22, r20, r7); // ldumaxl w22, w20, [x7] + __ swpl(Assembler::word, r14, r21, r11); // swpl w14, w21, [x11] + __ ldaddl(Assembler::word, r27, r13, r15); // ldaddl w27, w13, [x15] + __ ldbicl(Assembler::word, zr, r17, r14); // ldclrl wzr, w17, [x14] + __ ldeorl(Assembler::word, r3, r30, r16); // ldeorl w3, w30, [x16] + __ ldorrl(Assembler::word, r22, r20, r7); // ldsetl w22, w20, [x7] + __ ldsminl(Assembler::word, r20, r3, r1); // ldsminl w20, w3, [x1] + __ ldsmaxl(Assembler::word, r26, r19, r9); // ldsmaxl w26, w19, [x9] + __ lduminl(Assembler::word, r16, r17, r21); // lduminl w16, w17, [x21] + __ ldumaxl(Assembler::word, r0, r4, r2); // ldumaxl w0, w4, [x2] // SHA3SIMDOp - __ bcax(v20, __ T16B, v3, v1, v26); // bcax v20.16B, v3.16B, v1.16B, v26.16B - __ eor3(v19, __ T16B, v9, v16, v17); // eor3 v19.16B, v9.16B, v16.16B, v17.16B - __ rax1(v21, __ T2D, v0, v4); // rax1 v21.2D, v0.2D, v4.2D - __ xar(v2, __ T2D, v24, v14, 12); // xar v2.2D, v24.2D, v14.2D, #12 + __ bcax(v24, __ T16B, v14, v6, v11); // bcax v24.16B, v14.16B, v6.16B, v11.16B + __ eor3(v21, __ T16B, v14, v17, v30); // eor3 v21.16B, v14.16B, v17.16B, v30.16B + __ rax1(v12, __ T2D, v3, v3); // rax1 v12.2D, v3.2D, v3.2D + __ xar(v23, __ T2D, v9, v3, 49); // xar v23.2D, v9.2D, v3.2D, #49 // SHA512SIMDOp - __ sha512h(v11, __ T2D, v21, v14); // sha512h q11, q21, v14.2D - __ sha512h2(v17, __ T2D, v30, v12); // sha512h2 q17, q30, v12.2D - __ sha512su0(v3, __ T2D, v3); // sha512su0 v3.2D, v3.2D - __ sha512su1(v23, __ T2D, v9, v3); // sha512su1 v23.2D, v9.2D, v3.2D + __ sha512h(v28, __ T2D, v3, v19); // sha512h q28, q3, v19.2D + __ sha512h2(v23, __ T2D, v7, v26); // sha512h2 q23, q7, v26.2D + __ sha512su0(v21, __ T2D, v14); // sha512su0 v21.2D, v14.2D + __ sha512su1(v5, __ T2D, v8, v26); // sha512su1 v5.2D, v8.2D, v26.2D // SVEBinaryImmOp - __ sve_add(z24, __ D, 26u); // add z24.d, z24.d, #0x1a - __ sve_sub(z19, __ S, 62u); // sub z19.s, z19.s, #0x3e - __ sve_and(z26, __ S, 917504u); // and z26.s, z26.s, #0xe0000 - __ sve_eor(z8, __ D, 18442240474082197503u); // eor z8.d, z8.d, #0xfff0000000003fff - __ sve_orr(z18, __ S, 253952u); // orr z18.s, z18.s, #0x3e000 + __ sve_add(z5, __ S, 146u); // add z5.s, z5.s, #0x92 + __ sve_sub(z17, __ B, 31u); // sub z17.b, z17.b, #0x1f + __ sve_and(z9, __ S, 16744448u); // and z9.s, z9.s, #0xff8000 + __ sve_eor(z12, __ H, 33279u); // eor z12.h, z12.h, #0x81ff + __ sve_orr(z11, __ H, 49663u); // orr z11.h, z11.h, #0xc1ff // SVEBinaryImmOp - __ sve_add(z9, __ S, 97u); // add z9.s, z9.s, #0x61 - __ sve_sub(z8, __ H, 118u); // sub z8.h, z8.h, #0x76 - __ sve_and(z19, __ S, 1056980736u); // and z19.s, z19.s, #0x3f003f00 - __ sve_eor(z25, __ S, 3758350339u); // eor z25.s, z25.s, #0xe003e003 - __ sve_orr(z9, __ S, 4294459391u); // orr z9.s, z9.s, #0xfff83fff + __ sve_add(z31, __ S, 72u); // add z31.s, z31.s, #0x48 + __ sve_sub(z16, __ H, 218u); // sub z16.h, z16.h, #0xda + __ sve_and(z23, __ D, 562675075514368u); // and z23.d, z23.d, #0x1ffc000000000 + __ sve_eor(z8, __ B, 243u); // eor z8.b, z8.b, #0xf3 + __ sve_orr(z10, __ B, 239u); // orr z10.b, z10.b, #0xef // SVEBinaryImmOp - __ sve_add(z23, __ D, 183u); // add z23.d, z23.d, #0xb7 - __ sve_sub(z8, __ H, 41u); // sub z8.h, z8.h, #0x29 - __ sve_and(z28, __ D, 8064u); // and z28.d, z28.d, #0x1f80 - __ sve_eor(z15, __ D, 18428729675200069887u); // eor z15.d, z15.d, #0xffc00000000000ff - __ sve_orr(z0, __ B, 239u); // orr z0.b, z0.b, #0xef + __ sve_add(z22, __ S, 5u); // add z22.s, z22.s, #0x5 + __ sve_sub(z3, __ S, 209u); // sub z3.s, z3.s, #0xd1 + __ sve_and(z5, __ D, 17870287719452639231u); // and z5.d, z5.d, #0xf80003ffffffffff + __ sve_eor(z17, __ B, 128u); // eor z17.b, z17.b, #0x80 + __ sve_orr(z30, __ H, 49663u); // orr z30.h, z30.h, #0xc1ff // SVEBinaryImmOp - __ sve_add(z5, __ D, 243u); // add z5.d, z5.d, #0xf3 - __ sve_sub(z19, __ S, 8u); // sub z19.s, z19.s, #0x8 - __ sve_and(z13, __ H, 32256u); // and z13.h, z13.h, #0x7e00 - __ sve_eor(z0, __ S, 4294967293u); // eor z0.s, z0.s, #0xfffffffd - __ sve_orr(z21, __ S, 4294965263u); // orr z21.s, z21.s, #0xfffff80f + __ sve_add(z2, __ D, 168u); // add z2.d, z2.d, #0xa8 + __ sve_sub(z23, __ S, 240u); // sub z23.s, z23.s, #0xf0 + __ sve_and(z12, __ H, 1u); // and z12.h, z12.h, #0x1 + __ sve_eor(z15, __ S, 1u); // eor z15.s, z15.s, #0x1 + __ sve_orr(z19, __ D, 18446532967477018623u); // orr z19.d, z19.d, #0xffff3fffffffffff // SVEBinaryImmOp - __ sve_add(z12, __ H, 20u); // add z12.h, z12.h, #0x14 - __ sve_sub(z0, __ H, 190u); // sub z0.h, z0.h, #0xbe - __ sve_and(z23, __ B, 239u); // and z23.b, z23.b, #0xef - __ sve_eor(z27, __ D, 18442240474082197503u); // eor z27.d, z27.d, #0xfff0000000003fff - __ sve_orr(z22, __ B, 124u); // orr z22.b, z22.b, #0x7c + __ sve_add(z13, __ S, 179u); // add z13.s, z13.s, #0xb3 + __ sve_sub(z2, __ B, 88u); // sub z2.b, z2.b, #0x58 + __ sve_and(z20, __ H, 57855u); // and z20.h, z20.h, #0xe1ff + __ sve_eor(z24, __ H, 33279u); // eor z24.h, z24.h, #0x81ff + __ sve_orr(z20, __ S, 917504u); // orr z20.s, z20.s, #0xe0000 // SVEBinaryImmOp - __ sve_add(z20, __ H, 165u); // add z20.h, z20.h, #0xa5 - __ sve_sub(z24, __ D, 72u); // sub z24.d, z24.d, #0x48 - __ sve_and(z31, __ S, 4026535935u); // and z31.s, z31.s, #0xf0000fff - __ sve_eor(z21, __ B, 128u); // eor z21.b, z21.b, #0x80 - __ sve_orr(z30, __ S, 4294967293u); // orr z30.s, z30.s, #0xfffffffd + __ sve_add(z21, __ H, 247u); // add z21.h, z21.h, #0xf7 + __ sve_sub(z22, __ D, 253u); // sub z22.d, z22.d, #0xfd + __ sve_and(z26, __ S, 1610637312u); // and z26.s, z26.s, #0x60006000 + __ sve_eor(z11, __ H, 51199u); // eor z11.h, z11.h, #0xc7ff + __ sve_orr(z5, __ B, 128u); // orr z5.b, z5.b, #0x80 // SVEVectorOp - __ sve_add(z26, __ H, z18, z19); // add z26.h, z18.h, z19.h - __ sve_sub(z11, __ S, z13, z29); // sub z11.s, z13.s, z29.s - __ sve_fadd(z5, __ S, z1, z14); // fadd z5.s, z1.s, z14.s - __ sve_fmul(z2, __ S, z7, z10); // fmul z2.s, z7.s, z10.s - __ sve_fsub(z19, __ S, z4, z26); // fsub z19.s, z4.s, z26.s - __ sve_sqadd(z2, __ B, z3, z30); // sqadd z2.b, z3.b, z30.b - __ sve_sqsub(z20, __ D, z5, z20); // sqsub z20.d, z5.d, z20.d - __ sve_uqadd(z29, __ H, z13, z13); // uqadd z29.h, z13.h, z13.h - __ sve_uqsub(z14, __ H, z30, z1); // uqsub z14.h, z30.h, z1.h - __ sve_abs(z28, __ D, p0, z3); // abs z28.d, p0/m, z3.d - __ sve_add(z9, __ B, p6, z9); // add z9.b, p6/m, z9.b, z9.b - __ sve_and(z26, __ B, p2, z14); // and z26.b, p2/m, z26.b, z14.b - __ sve_asr(z20, __ D, p6, z7); // asr z20.d, p6/m, z20.d, z7.d - __ sve_bic(z20, __ D, p4, z6); // bic z20.d, p4/m, z20.d, z6.d - __ sve_clz(z13, __ H, p0, z29); // clz z13.h, p0/m, z29.h - __ sve_cnt(z9, __ B, p0, z1); // cnt z9.b, p0/m, z1.b - __ sve_eor(z27, __ B, p6, z15); // eor z27.b, p6/m, z27.b, z15.b - __ sve_lsl(z4, __ D, p7, z17); // lsl z4.d, p7/m, z4.d, z17.d - __ sve_lsr(z2, __ B, p0, z24); // lsr z2.b, p0/m, z2.b, z24.b - __ sve_mul(z26, __ B, p7, z13); // mul z26.b, p7/m, z26.b, z13.b - __ sve_neg(z22, __ D, p3, z16); // neg z22.d, p3/m, z16.d - __ sve_not(z17, __ D, p1, z11); // not z17.d, p1/m, z11.d - __ sve_orr(z16, __ B, p0, z16); // orr z16.b, p0/m, z16.b, z16.b - __ sve_rbit(z28, __ D, p1, z23); // rbit z28.d, p1/m, z23.d - __ sve_revb(z28, __ D, p4, z10); // revb z28.d, p4/m, z10.d - __ sve_smax(z17, __ S, p7, z7); // smax z17.s, p7/m, z17.s, z7.s - __ sve_smin(z4, __ H, p3, z24); // smin z4.h, p3/m, z4.h, z24.h - __ sve_umax(z9, __ B, p2, z11); // umax z9.b, p2/m, z9.b, z11.b - __ sve_umin(z4, __ S, p5, z22); // umin z4.s, p5/m, z4.s, z22.s - __ sve_sub(z4, __ H, p0, z15); // sub z4.h, p0/m, z4.h, z15.h - __ sve_fabs(z4, __ D, p7, z26); // fabs z4.d, p7/m, z26.d - __ sve_fadd(z5, __ S, p5, z26); // fadd z5.s, p5/m, z5.s, z26.s - __ sve_fdiv(z31, __ S, p0, z25); // fdiv z31.s, p0/m, z31.s, z25.s - __ sve_fmax(z8, __ D, p1, z3); // fmax z8.d, p1/m, z8.d, z3.d - __ sve_fmin(z7, __ D, p6, z24); // fmin z7.d, p6/m, z7.d, z24.d - __ sve_fmul(z24, __ S, p7, z17); // fmul z24.s, p7/m, z24.s, z17.s - __ sve_fneg(z10, __ S, p3, z30); // fneg z10.s, p3/m, z30.s - __ sve_frintm(z8, __ S, p6, z29); // frintm z8.s, p6/m, z29.s - __ sve_frintn(z31, __ D, p5, z31); // frintn z31.d, p5/m, z31.d - __ sve_frintp(z0, __ D, p5, z7); // frintp z0.d, p5/m, z7.d - __ sve_fsqrt(z29, __ S, p6, z22); // fsqrt z29.s, p6/m, z22.s - __ sve_fsub(z29, __ S, p6, z20); // fsub z29.s, p6/m, z29.s, z20.s - __ sve_fmad(z6, __ D, p4, z18, z13); // fmad z6.d, p4/m, z18.d, z13.d - __ sve_fmla(z21, __ S, p2, z0, z19); // fmla z21.s, p2/m, z0.s, z19.s - __ sve_fmls(z28, __ D, p1, z17, z6); // fmls z28.d, p1/m, z17.d, z6.d - __ sve_fmsb(z20, __ D, p6, z28, z14); // fmsb z20.d, p6/m, z28.d, z14.d - __ sve_fnmad(z14, __ S, p4, z10, z26); // fnmad z14.s, p4/m, z10.s, z26.s - __ sve_fnmsb(z24, __ D, p0, z11, z15); // fnmsb z24.d, p0/m, z11.d, z15.d - __ sve_fnmla(z23, __ D, p5, z20, z28); // fnmla z23.d, p5/m, z20.d, z28.d - __ sve_fnmls(z20, __ D, p7, z24, z0); // fnmls z20.d, p7/m, z24.d, z0.d - __ sve_mla(z6, __ B, p5, z13, z12); // mla z6.b, p5/m, z13.b, z12.b - __ sve_mls(z13, __ S, p7, z26, z23); // mls z13.s, p7/m, z26.s, z23.s - __ sve_and(z6, z2, z29); // and z6.d, z2.d, z29.d - __ sve_eor(z0, z29, z23); // eor z0.d, z29.d, z23.d - __ sve_orr(z4, z5, z8); // orr z4.d, z5.d, z8.d - __ sve_bic(z13, z17, z13); // bic z13.d, z17.d, z13.d - __ sve_uzp1(z8, __ H, z10, z8); // uzp1 z8.h, z10.h, z8.h - __ sve_uzp2(z19, __ S, z0, z29); // uzp2 z19.s, z0.s, z29.s - __ sve_fabd(z16, __ D, p3, z23); // fabd z16.d, p3/m, z16.d, z23.d - __ sve_bext(z23, __ B, z30, z13); // bext z23.b, z30.b, z13.b - __ sve_bdep(z25, __ H, z22, z0); // bdep z25.h, z22.h, z0.h - __ sve_eor3(z25, z30, z11); // eor3 z25.d, z25.d, z30.d, z11.d - __ sve_sqadd(z14, __ H, p5, z22); // sqadd z14.h, p5/m, z14.h, z22.h - __ sve_sqsub(z5, __ H, p4, z0); // sqsub z5.h, p4/m, z5.h, z0.h - __ sve_uqadd(z9, __ D, p0, z3); // uqadd z9.d, p0/m, z9.d, z3.d - __ sve_uqsub(z14, __ H, p1, z29); // uqsub z14.h, p1/m, z14.h, z29.h + __ sve_add(z2, __ H, z7, z10); // add z2.h, z7.h, z10.h + __ sve_sub(z19, __ H, z4, z26); // sub z19.h, z4.h, z26.h + __ sve_fadd(z2, __ S, z3, z30); // fadd z2.s, z3.s, z30.s + __ sve_fmul(z20, __ D, z5, z20); // fmul z20.d, z5.d, z20.d + __ sve_fsub(z29, __ S, z13, z13); // fsub z29.s, z13.s, z13.s + __ sve_sqadd(z14, __ H, z30, z1); // sqadd z14.h, z30.h, z1.h + __ sve_sqsub(z28, __ D, z3, z3); // sqsub z28.d, z3.d, z3.d + __ sve_uqadd(z9, __ B, z25, z9); // uqadd z9.b, z25.b, z9.b + __ sve_uqsub(z26, __ B, z10, z14); // uqsub z26.b, z10.b, z14.b + __ sve_abs(z20, __ D, p6, z7); // abs z20.d, p6/m, z7.d + __ sve_add(z20, __ D, p4, z6); // add z20.d, p4/m, z20.d, z6.d + __ sve_and(z13, __ H, p0, z29); // and z13.h, p0/m, z13.h, z29.h + __ sve_asr(z9, __ B, p0, z1); // asr z9.b, p0/m, z9.b, z1.b + __ sve_bic(z27, __ B, p6, z15); // bic z27.b, p6/m, z27.b, z15.b + __ sve_clz(z4, __ D, p7, z17); // clz z4.d, p7/m, z17.d + __ sve_cnt(z2, __ B, p0, z24); // cnt z2.b, p0/m, z24.b + __ sve_eor(z26, __ B, p7, z13); // eor z26.b, p7/m, z26.b, z13.b + __ sve_lsl(z22, __ D, p3, z16); // lsl z22.d, p3/m, z22.d, z16.d + __ sve_lsr(z17, __ D, p1, z11); // lsr z17.d, p1/m, z17.d, z11.d + __ sve_mul(z16, __ B, p0, z16); // mul z16.b, p0/m, z16.b, z16.b + __ sve_neg(z28, __ D, p1, z23); // neg z28.d, p1/m, z23.d + __ sve_not(z28, __ S, p4, z10); // not z28.s, p4/m, z10.s + __ sve_orr(z17, __ S, p7, z7); // orr z17.s, p7/m, z17.s, z7.s + __ sve_rbit(z4, __ H, p3, z24); // rbit z4.h, p3/m, z24.h + __ sve_revb(z9, __ H, p2, z11); // revb z9.h, p2/m, z11.h + __ sve_smax(z4, __ S, p5, z22); // smax z4.s, p5/m, z4.s, z22.s + __ sve_smin(z4, __ H, p0, z15); // smin z4.h, p0/m, z4.h, z15.h + __ sve_umax(z4, __ D, p7, z26); // umax z4.d, p7/m, z4.d, z26.d + __ sve_umin(z5, __ H, p5, z26); // umin z5.h, p5/m, z5.h, z26.h + __ sve_sub(z31, __ B, p0, z25); // sub z31.b, p0/m, z31.b, z25.b + __ sve_fabs(z8, __ D, p1, z3); // fabs z8.d, p1/m, z3.d + __ sve_fadd(z7, __ D, p6, z24); // fadd z7.d, p6/m, z7.d, z24.d + __ sve_fdiv(z24, __ S, p7, z17); // fdiv z24.s, p7/m, z24.s, z17.s + __ sve_fmax(z10, __ S, p3, z30); // fmax z10.s, p3/m, z10.s, z30.s + __ sve_fmin(z8, __ S, p6, z29); // fmin z8.s, p6/m, z8.s, z29.s + __ sve_fmul(z31, __ D, p5, z31); // fmul z31.d, p5/m, z31.d, z31.d + __ sve_fneg(z0, __ D, p5, z7); // fneg z0.d, p5/m, z7.d + __ sve_frintm(z29, __ S, p6, z22); // frintm z29.s, p6/m, z22.s + __ sve_frintn(z29, __ S, p6, z20); // frintn z29.s, p6/m, z20.s + __ sve_frintp(z6, __ S, p4, z18); // frintp z6.s, p4/m, z18.s + __ sve_fsqrt(z26, __ S, p5, z8); // fsqrt z26.s, p5/m, z8.s + __ sve_fsub(z19, __ S, p2, z28); // fsub z19.s, p2/m, z19.s, z28.s + __ sve_fmad(z17, __ D, p1, z30, z20); // fmad z17.d, p1/m, z30.d, z20.d + __ sve_fmla(z28, __ D, p3, z17, z14); // fmla z28.d, p3/m, z17.d, z14.d + __ sve_fmls(z10, __ S, p6, z11, z24); // fmls z10.s, p6/m, z11.s, z24.s + __ sve_fmsb(z11, __ D, p3, z28, z23); // fmsb z11.d, p3/m, z28.d, z23.d + __ sve_fnmad(z20, __ D, p7, z23, z20); // fnmad z20.d, p7/m, z23.d, z20.d + __ sve_fnmsb(z24, __ D, p0, z27, z6); // fnmsb z24.d, p0/m, z27.d, z6.d + __ sve_fnmla(z13, __ D, p3, z4, z13); // fnmla z13.d, p3/m, z4.d, z13.d + __ sve_fnmls(z26, __ S, p5, z20, z6); // fnmls z26.s, p5/m, z20.s, z6.s + __ sve_mla(z29, __ S, p7, z0, z29); // mla z29.s, p7/m, z0.s, z29.s + __ sve_mls(z3, __ D, p1, z5, z8); // mls z3.d, p1/m, z5.d, z8.d + __ sve_and(z13, z17, z13); // and z13.d, z17.d, z13.d + __ sve_eor(z8, z10, z8); // eor z8.d, z10.d, z8.d + __ sve_orr(z19, z0, z29); // orr z19.d, z0.d, z29.d + __ sve_bic(z16, z13, z23); // bic z16.d, z13.d, z23.d + __ sve_uzp1(z23, __ B, z30, z13); // uzp1 z23.b, z30.b, z13.b + __ sve_uzp2(z25, __ H, z22, z0); // uzp2 z25.h, z22.h, z0.h + __ sve_fabd(z25, __ S, p7, z11); // fabd z25.s, p7/m, z25.s, z11.s + __ sve_bext(z14, __ H, z23, z22); // bext z14.h, z23.h, z22.h + __ sve_bdep(z5, __ H, z18, z0); // bdep z5.h, z18.h, z0.h + __ sve_eor3(z9, z2, z3); // eor3 z9.d, z9.d, z2.d, z3.d + __ sve_sqadd(z14, __ H, p1, z29); // sqadd z14.h, p1/m, z14.h, z29.h + __ sve_sqsub(z14, __ D, p5, z4); // sqsub z14.d, p5/m, z14.d, z4.d + __ sve_uqadd(z27, __ S, p3, z22); // uqadd z27.s, p3/m, z27.s, z22.s + __ sve_uqsub(z31, __ S, p6, z11); // uqsub z31.s, p6/m, z31.s, z11.s // SVEReductionOp - __ sve_andv(v14, __ D, p5, z4); // andv d14, p5, z4.d - __ sve_orv(v27, __ S, p3, z22); // orv s27, p3, z22.s - __ sve_eorv(v31, __ S, p6, z11); // eorv s31, p6, z11.s - __ sve_smaxv(v12, __ B, p4, z28); // smaxv b12, p4, z28.b - __ sve_sminv(v28, __ D, p4, z4); // sminv d28, p4, z4.d - __ sve_fminv(v6, __ D, p0, z15); // fminv d6, p0, z15.d - __ sve_fmaxv(v1, __ D, p5, z18); // fmaxv d1, p5, z18.d - __ sve_fadda(v2, __ S, p2, z4); // fadda s2, p2, s2, z4.s - __ sve_uaddv(v11, __ S, p2, z28); // uaddv d11, p2, z28.s + __ sve_andv(v12, __ B, p4, z28); // andv b12, p4, z28.b + __ sve_orv(v28, __ D, p4, z4); // orv d28, p4, z4.d + __ sve_eorv(v6, __ S, p0, z15); // eorv s6, p0, z15.s + __ sve_smaxv(v1, __ S, p5, z18); // smaxv s1, p5, z18.s + __ sve_sminv(v2, __ H, p2, z4); // sminv h2, p2, z4.h + __ sve_umaxv(v11, __ S, p2, z28); // umaxv s11, p2, z28.s + __ sve_uminv(v3, __ H, p5, z31); // uminv h3, p5, z31.h + __ sve_fminv(v24, __ S, p5, z15); // fminv s24, p5, z15.s + __ sve_fmaxv(v6, __ S, p3, z8); // fmaxv s6, p3, z8.s + __ sve_fadda(v21, __ D, p7, z4); // fadda d21, p7, d21, z4.d + __ sve_uaddv(v24, __ B, p5, z6); // uaddv d24, p5, z6.b // AddWideNEONOp - __ saddwv(v3, v4, __ T8H, v5, __ T8B); // saddw v3.8H, v4.8H, v5.8B - __ saddwv2(v21, v22, __ T8H, v23, __ T16B); // saddw2 v21.8H, v22.8H, v23.16B - __ saddwv(v31, v0, __ T4S, v1, __ T4H); // saddw v31.4S, v0.4S, v1.4H - __ saddwv2(v11, v12, __ T4S, v13, __ T8H); // saddw2 v11.4S, v12.4S, v13.8H - __ saddwv(v24, v25, __ T2D, v26, __ T2S); // saddw v24.2D, v25.2D, v26.2S - __ saddwv2(v21, v22, __ T2D, v23, __ T4S); // saddw2 v21.2D, v22.2D, v23.4S - __ uaddwv(v15, v16, __ T8H, v17, __ T8B); // uaddw v15.8H, v16.8H, v17.8B - __ uaddwv2(v12, v13, __ T8H, v14, __ T16B); // uaddw2 v12.8H, v13.8H, v14.16B - __ uaddwv(v6, v7, __ T4S, v8, __ T4H); // uaddw v6.4S, v7.4S, v8.4H - __ uaddwv2(v13, v14, __ T4S, v15, __ T8H); // uaddw2 v13.4S, v14.4S, v15.8H - __ uaddwv(v8, v9, __ T2D, v10, __ T2S); // uaddw v8.2D, v9.2D, v10.2S - __ uaddwv2(v15, v16, __ T2D, v17, __ T4S); // uaddw2 v15.2D, v16.2D, v17.4S + __ saddwv(v4, v5, __ T8H, v6, __ T8B); // saddw v4.8H, v5.8H, v6.8B + __ saddwv2(v10, v11, __ T8H, v12, __ T16B); // saddw2 v10.8H, v11.8H, v12.16B + __ saddwv(v9, v10, __ T4S, v11, __ T4H); // saddw v9.4S, v10.4S, v11.4H + __ saddwv2(v25, v26, __ T4S, v27, __ T8H); // saddw2 v25.4S, v26.4S, v27.8H + __ saddwv(v10, v11, __ T2D, v12, __ T2S); // saddw v10.2D, v11.2D, v12.2S + __ saddwv2(v5, v6, __ T2D, v7, __ T4S); // saddw2 v5.2D, v6.2D, v7.4S + __ uaddwv(v31, v0, __ T8H, v1, __ T8B); // uaddw v31.8H, v0.8H, v1.8B + __ uaddwv2(v22, v23, __ T8H, v24, __ T16B); // uaddw2 v22.8H, v23.8H, v24.16B + __ uaddwv(v25, v26, __ T4S, v27, __ T4H); // uaddw v25.4S, v26.4S, v27.4H + __ uaddwv2(v15, v16, __ T4S, v17, __ T8H); // uaddw2 v15.4S, v16.4S, v17.8H + __ uaddwv(v3, v4, __ T2D, v5, __ T2S); // uaddw v3.2D, v4.2D, v5.2S + __ uaddwv2(v18, v19, __ T2D, v20, __ T4S); // uaddw2 v18.2D, v19.2D, v20.4S __ bind(forth); @@ -1449,30 +1463,30 @@ 0x9101a1a0, 0xb10a5cc8, 0xd10810aa, 0xf10fd061, 0x120cb166, 0x321764bc, 0x52174681, 0x720c0227, 0x9241018e, 0xb25a2969, 0xd278b411, 0xf26aad01, - 0x14000000, 0x17ffffd7, 0x140004bb, 0x94000000, - 0x97ffffd4, 0x940004b8, 0x3400000a, 0x34fffa2a, - 0x340096aa, 0x35000008, 0x35fff9c8, 0x35009648, - 0xb400000b, 0xb4fff96b, 0xb40095eb, 0xb500001d, - 0xb5fff91d, 0xb500959d, 0x10000013, 0x10fff8b3, - 0x10009533, 0x90000013, 0x36300016, 0x3637f836, - 0x363094b6, 0x3758000c, 0x375ff7cc, 0x3758944c, + 0x14000000, 0x17ffffd7, 0x140004c9, 0x94000000, + 0x97ffffd4, 0x940004c6, 0x3400000a, 0x34fffa2a, + 0x3400986a, 0x35000008, 0x35fff9c8, 0x35009808, + 0xb400000b, 0xb4fff96b, 0xb40097ab, 0xb500001d, + 0xb5fff91d, 0xb500975d, 0x10000013, 0x10fff8b3, + 0x100096f3, 0x90000013, 0x36300016, 0x3637f836, + 0x36309676, 0x3758000c, 0x375ff7cc, 0x3758960c, 0x128313a0, 0x528a32c7, 0x7289173b, 0x92ab3acc, 0xd2a0bf94, 0xf2c285e8, 0x9358722f, 0x330e652f, 0x53067f3b, 0x93577c53, 0xb34a1aac, 0xd35a4016, 0x13946c63, 0x93c3dbc8, 0x54000000, 0x54fff5a0, - 0x54009220, 0x54000001, 0x54fff541, 0x540091c1, - 0x54000002, 0x54fff4e2, 0x54009162, 0x54000002, - 0x54fff482, 0x54009102, 0x54000003, 0x54fff423, - 0x540090a3, 0x54000003, 0x54fff3c3, 0x54009043, - 0x54000004, 0x54fff364, 0x54008fe4, 0x54000005, - 0x54fff305, 0x54008f85, 0x54000006, 0x54fff2a6, - 0x54008f26, 0x54000007, 0x54fff247, 0x54008ec7, - 0x54000008, 0x54fff1e8, 0x54008e68, 0x54000009, - 0x54fff189, 0x54008e09, 0x5400000a, 0x54fff12a, - 0x54008daa, 0x5400000b, 0x54fff0cb, 0x54008d4b, - 0x5400000c, 0x54fff06c, 0x54008cec, 0x5400000d, - 0x54fff00d, 0x54008c8d, 0x5400000e, 0x54ffefae, - 0x54008c2e, 0x5400000f, 0x54ffef4f, 0x54008bcf, + 0x540093e0, 0x54000001, 0x54fff541, 0x54009381, + 0x54000002, 0x54fff4e2, 0x54009322, 0x54000002, + 0x54fff482, 0x540092c2, 0x54000003, 0x54fff423, + 0x54009263, 0x54000003, 0x54fff3c3, 0x54009203, + 0x54000004, 0x54fff364, 0x540091a4, 0x54000005, + 0x54fff305, 0x54009145, 0x54000006, 0x54fff2a6, + 0x540090e6, 0x54000007, 0x54fff247, 0x54009087, + 0x54000008, 0x54fff1e8, 0x54009028, 0x54000009, + 0x54fff189, 0x54008fc9, 0x5400000a, 0x54fff12a, + 0x54008f6a, 0x5400000b, 0x54fff0cb, 0x54008f0b, + 0x5400000c, 0x54fff06c, 0x54008eac, 0x5400000d, + 0x54fff00d, 0x54008e4d, 0x5400000e, 0x54ffefae, + 0x54008dee, 0x5400000f, 0x54ffef4f, 0x54008d8f, 0xd40658e1, 0xd4014d22, 0xd4046543, 0xd4273f60, 0xd44cad80, 0xd503201f, 0xd503203f, 0xd503205f, 0xd503209f, 0xd50320bf, 0xd503219f, 0xd50323bf, @@ -1613,30 +1627,33 @@ 0x0eb26e30, 0x4eae6dac, 0x2e2d6d8b, 0x6e2b6d49, 0x2e686ce6, 0x6e606ffe, 0x2eb36e51, 0x6ebd6f9b, 0x0e3eafbc, 0x4e20affe, 0x0e69ad07, 0x4e6cad6a, - 0x0eb6aeb4, 0x4eacad6a, 0x0e66b4a4, 0x4e7ab738, - 0x0eb3b651, 0x4eb3b651, 0x0e3826f6, 0x4e252483, - 0x0e7f27dd, 0x4e71260f, 0x0eb826f6, 0x4eb52693, - 0x0eb5f693, 0x4eb8f6f6, 0x4ee4f462, 0x0ed1360f, - 0x4ec834e6, 0x2eaeedac, 0x6eb2ee30, 0x6eeded8b, - 0x2ecf2dcd, 0x6ed92f17, 0x0f81100f, 0x4f848862, - 0x4fc31841, 0x0fad518b, 0x4fa780c5, 0x4fd059ee, - 0x2fa890e6, 0x4fa38841, 0x6fc1900f, 0x0f7b8149, - 0x4f4688a4, 0x0faf81cd, 0x4fa58083, 0x0e3736d5, - 0x4e393717, 0x0e61341f, 0x4e7b3759, 0x0ea43462, - 0x4ea1341f, 0x4efd379b, 0x0e343e72, 0x4e2c3d6a, - 0x0e793f17, 0x4e753e93, 0x0ea53c83, 0x4eb43e72, - 0x4ee23c20, 0x2e3b8f59, 0x6e3c8f7a, 0x2e798f17, - 0x6e648c62, 0x2eb48e72, 0x6eae8dac, 0x6ee68ca4, - 0x2e3e37bc, 0x6e2037fe, 0x2e7f37dd, 0x6e723630, - 0x2ebd379b, 0x6ea834e6, 0x6eeb3549, 0x2e3f3fdd, - 0x6e343e72, 0x2e693d07, 0x6e663ca4, 0x2ea93d07, - 0x6eb13e0f, 0x6eeb3d49, 0x0e39e717, 0x4e2ae528, - 0x4e64e462, 0x2ebee7bc, 0x6eb7e6d5, 0x6ee1e41f, - 0x2e27e4c5, 0x6e3de79b, 0x6e62e420, 0x659239e8, - 0x65d03b94, 0x65d0232d, 0x65d120c2, 0x659129f2, - 0x65933ca3, 0x25969683, 0x25961d15, 0x254d1c48, - 0x259e3f61, 0x25953b96, 0x255b91d1, 0x247686ed, - 0x24309098, 0x2462edb9, 0x24a57468, 0xba5fd3e3, + 0x0eb6aeb4, 0x4eacad6a, 0x2e26aca4, 0x6e3aaf38, + 0x2e73ae51, 0x6e73ae51, 0x2eb8aef6, 0x6ea5ac83, + 0x2e3fa7dd, 0x6e31a60f, 0x2e78a6f6, 0x6e75a693, + 0x2eb5a693, 0x6eb8a6f6, 0x0e64b462, 0x4e71b60f, + 0x0ea8b4e6, 0x4eaeb5ac, 0x0e322630, 0x4e2d258b, + 0x0e6f25cd, 0x4e792717, 0x0ea32441, 0x4ea027fe, + 0x0eb5f693, 0x4ea7f4c5, 0x4ef3f651, 0x0ec43462, + 0x4ed23630, 0x2eb8eef6, 0x6eafedcd, 0x6eeced6a, + 0x2ed72ed5, 0x6edf2fdd, 0x0fa810e6, 0x4fa38841, + 0x4fc1100f, 0x0fab5149, 0x4f8688a4, 0x4fcf59cd, + 0x2f859083, 0x4f8d898b, 0x6fce99ac, 0x0f41800f, + 0x4f6b8949, 0x0f8d818b, 0x4f838841, 0x0e343672, + 0x4e223420, 0x0e7b3759, 0x4e7c377a, 0x0eb93717, + 0x4ea43462, 0x4ef43672, 0x0e2e3dac, 0x4e263ca4, + 0x0e7e3fbc, 0x4e603ffe, 0x0ebf3fdd, 0x4eb23e30, + 0x4efd3f9b, 0x2e288ce6, 0x6e2b8d49, 0x2e7f8fdd, + 0x6e748e72, 0x2ea98d07, 0x6ea68ca4, 0x6ee98d07, + 0x2e31360f, 0x6e2b3549, 0x2e793717, 0x6e6a3528, + 0x2ea43462, 0x6ebe37bc, 0x6ef736d5, 0x2e213c1f, + 0x6e273cc5, 0x2e7d3f9b, 0x6e623c20, 0x2eb33e51, + 0x6eb13e0f, 0x6ee63ca4, 0x0e3ce77a, 0x4e2ae528, + 0x4e7ee7bc, 0x2eb8e6f6, 0x6ebde79b, 0x6efde79b, + 0x2e3be759, 0x6e39e717, 0x6e62e420, 0x65d220c2, + 0x659029f2, 0x65903ca3, 0x65d13683, 0x65913173, + 0x65d320cf, 0x25cb83c6, 0x254c0fbb, 0x251e0308, + 0x25473a05, 0x25943096, 0x259d9270, 0x2423d187, + 0x242786fa, 0x24f0f1b9, 0x24e42043, 0xba5fd3e3, 0x3a5f03e5, 0xfa411be4, 0x7a42cbe2, 0x93df03ff, 0xc820ffff, 0x8822fc7f, 0xc8247cbf, 0x88267fff, 0x4e010fe0, 0x5e040420, 0x4e081fe1, 0x4e0c1fe1, @@ -1703,55 +1720,55 @@ 0x1e741000, 0x1e743000, 0x1e761000, 0x1e763000, 0x1e781000, 0x1e783000, 0x1e7a1000, 0x1e7a3000, 0x1e7c1000, 0x1e7c3000, 0x1e7e1000, 0x1e7e3000, - 0xf8268267, 0xf82d023c, 0xf8301046, 0xf83d2083, - 0xf8263290, 0xf82d528c, 0xf8284299, 0xf8337160, - 0xf8386286, 0xf8bf820e, 0xf8a600e0, 0xf8af1353, - 0xf8a922ea, 0xf8b53396, 0xf8a251e3, 0xf8b340f4, - 0xf8a470fd, 0xf8a06209, 0xf8f48097, 0xf8f002ea, - 0xf8eb10d9, 0xf8ff21b0, 0xf8f7302c, 0xf8ee52a9, - 0xf8f041fa, 0xf8e471e4, 0xf8e863c6, 0xf864823d, - 0xf87d013a, 0xf86f1162, 0xf87d20e3, 0xf86132bb, - 0xf870510e, 0xf8704336, 0xf86572b4, 0xf8706217, - 0xb83e8294, 0xb8200264, 0xb8381284, 0xb8242358, - 0xb8333102, 0xb828530e, 0xb83042df, 0xb824703f, - 0xb82a6194, 0xb8a080e9, 0xb8b80090, 0xb8bb1146, - 0xb8bb21b8, 0xb8b032df, 0xb8b653f4, 0xb8bd41c9, - 0xb8b47287, 0xb8bc6169, 0xb8ee828c, 0xb8e10138, - 0xb8f3126d, 0xb8f020b0, 0xb8e03183, 0xb8e851ef, - 0xb8f041e4, 0xb8fe7005, 0xb8ea6376, 0xb8638120, - 0xb873015d, 0xb8781284, 0xb86723b8, 0xb86e3175, - 0xb87b51ed, 0xb87f41d1, 0xb863721e, 0xb87660f4, - 0xce216874, 0xce104533, 0xce648c15, 0xce8e3302, - 0xce6e82ab, 0xce6c87d1, 0xcec08063, 0xce638937, - 0x25e0c358, 0x25a1c7d3, 0x0580785a, 0x05426328, - 0x05009892, 0x25a0cc29, 0x2561cec8, 0x058044b3, - 0x05401c99, 0x05006b49, 0x25e0d6f7, 0x2561c528, - 0x0583c8bc, 0x0542522f, 0x05001ec0, 0x25e0de65, - 0x25a1c113, 0x05803cad, 0x0540f3c0, 0x0500ab15, - 0x2560c28c, 0x2561d7c0, 0x05801ed7, 0x0542633b, - 0x05003696, 0x2560d4b4, 0x25e1c918, 0x058021ff, - 0x05400e15, 0x0500f3de, 0x0473025a, 0x04bd05ab, - 0x658e0025, 0x658a08e2, 0x659a0493, 0x043e1062, - 0x04f418b4, 0x046d15bd, 0x04611fce, 0x04d6a07c, - 0x04001929, 0x041a09da, 0x04d098f4, 0x04db10d4, - 0x0459a3ad, 0x041aa029, 0x041919fb, 0x04d39e24, - 0x04118302, 0x04101dba, 0x04d7ae16, 0x04dea571, - 0x04180210, 0x05e786fc, 0x05e4915c, 0x04881cf1, - 0x044a0f04, 0x04090969, 0x048b16c4, 0x044101e4, - 0x04dcbf44, 0x65809745, 0x658d833f, 0x65c68468, - 0x65c79b07, 0x65829e38, 0x049dafca, 0x6582bba8, - 0x65c0b7ff, 0x65c1b4e0, 0x658dbadd, 0x65819a9d, - 0x65ed9246, 0x65b30815, 0x65e6263c, 0x65eebb94, - 0x65bad14e, 0x65efe178, 0x65fc5697, 0x65e07f14, - 0x040c55a6, 0x04977f4d, 0x043d3046, 0x04b733a0, - 0x046830a4, 0x04ed322d, 0x05686948, 0x05bd6c13, - 0x65c88ef0, 0x450db3d7, 0x4540b6d9, 0x043e3979, - 0x445896ce, 0x445a9005, 0x44d98069, 0x445b87ae, - 0x04da348e, 0x04982edb, 0x0499397f, 0x0408338c, - 0x04ca309c, 0x65c721e6, 0x65c63641, 0x65982882, - 0x04812b8b, 0x0e251083, 0x4e3712d5, 0x0e61101f, - 0x4e6d118b, 0x0eba1338, 0x4eb712d5, 0x2e31120f, - 0x6e2e11ac, 0x2e6810e6, 0x6e6f11cd, 0x2eaa1128, - 0x6eb1120f, + 0xf8268290, 0xf82d028c, 0xf8281299, 0xf8332160, + 0xf8383286, 0xf83f520e, 0xf82640e0, 0xf82f7353, + 0xf82962ea, 0xf8b58396, 0xf8a201e3, 0xf8b310f4, + 0xf8a420fd, 0xf8a03209, 0xf8b45097, 0xf8b042ea, + 0xf8ab70d9, 0xf8bf61b0, 0xf8f7802c, 0xf8ee02a9, + 0xf8f011fa, 0xf8e421e4, 0xf8e833c6, 0xf8e4523d, + 0xf8fd413a, 0xf8ef7162, 0xf8fd60e3, 0xf86182bb, + 0xf870010e, 0xf8701336, 0xf86522b4, 0xf8703217, + 0xf87e5294, 0xf8604264, 0xf8787284, 0xf8646358, + 0xb8338102, 0xb828030e, 0xb83012df, 0xb824203f, + 0xb82a3194, 0xb82050e9, 0xb8384090, 0xb83b7146, + 0xb83b61b8, 0xb8b082df, 0xb8b603f4, 0xb8bd11c9, + 0xb8b42287, 0xb8bc3169, 0xb8ae528c, 0xb8a14138, + 0xb8b3726d, 0xb8b060b0, 0xb8e08183, 0xb8e801ef, + 0xb8f011e4, 0xb8fe2005, 0xb8ea3376, 0xb8e35120, + 0xb8f3415d, 0xb8f87284, 0xb8e763b8, 0xb86e8175, + 0xb87b01ed, 0xb87f11d1, 0xb863221e, 0xb87630f4, + 0xb8745023, 0xb87a4133, 0xb87072b1, 0xb8606044, + 0xce262dd8, 0xce1179d5, 0xce638c6c, 0xce83c537, + 0xce73807c, 0xce7a84f7, 0xcec081d5, 0xce7a8905, + 0x25a0d245, 0x2521c3f1, 0x05808909, 0x05400d2c, + 0x0500154b, 0x25a0c91f, 0x2561db50, 0x0582d157, + 0x054026a8, 0x05001eca, 0x25a0c0b6, 0x25a1da23, + 0x05822dc5, 0x05400e11, 0x0500155e, 0x25e0d502, + 0x25a1de17, 0x0580040c, 0x0540000f, 0x050287b3, + 0x25a0d66d, 0x2521cb02, 0x05801d74, 0x05400d38, + 0x05007854, 0x2560def5, 0x25e1dfb6, 0x05801c3a, + 0x0540158b, 0x05000e05, 0x046a00e2, 0x047a0493, + 0x659e0062, 0x65d408b4, 0x658d05bd, 0x046113ce, + 0x04e3187c, 0x04291729, 0x042e1d5a, 0x04d6b8f4, + 0x04c010d4, 0x045a03ad, 0x04108029, 0x041b19fb, + 0x04d9be24, 0x041aa302, 0x04191dba, 0x04d38e16, + 0x04d18571, 0x04100210, 0x04d7a6fc, 0x049eb15c, + 0x04981cf1, 0x05678f04, 0x05648969, 0x048816c4, + 0x044a01e4, 0x04c91f44, 0x044b1745, 0x0401033f, + 0x04dca468, 0x65c09b07, 0x658d9e38, 0x65868fca, + 0x65879ba8, 0x65c297ff, 0x04ddb4e0, 0x6582badd, + 0x6580ba9d, 0x6581b246, 0x658db51a, 0x65818b93, + 0x65f487d1, 0x65ee0e3c, 0x65b8396a, 0x65f7af8b, + 0x65f4def4, 0x65e6e378, 0x65ed4c8d, 0x65a6769a, + 0x049d5c1d, 0x04c864a3, 0x042d322d, 0x04a83148, + 0x047d3013, 0x04f731b0, 0x052d6bd7, 0x05606ed9, + 0x65889d79, 0x4556b2ee, 0x4540b645, 0x04223869, + 0x445887ae, 0x44da948e, 0x44998edb, 0x449b997f, + 0x041a338c, 0x04d8309c, 0x049921e6, 0x04883641, + 0x044a2882, 0x04892b8b, 0x044b37e3, 0x658735f8, + 0x65862d06, 0x65d83c95, 0x040134d8, 0x0e2610a4, + 0x4e2c116a, 0x0e6b1149, 0x4e7b1359, 0x0eac116a, + 0x4ea710c5, 0x2e21101f, 0x6e3812f6, 0x2e7b1359, + 0x6e71120f, 0x2ea51083, 0x6eb41272, }; // END Generated code -- do not edit diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 3fd8e2eceb4..16f4b7847a2 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.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 @@ -1574,6 +1574,16 @@ public class IRNode { superWordNodes(MAX_REDUCTION_V, "MaxReductionV"); } + public static final String UMIN_REDUCTION_V = PREFIX + "UMIN_REDUCTION_V" + POSTFIX; + static { + superWordNodes(UMIN_REDUCTION_V, "UMinReductionV"); + } + + public static final String UMAX_REDUCTION_V = PREFIX + "UMAX_REDUCTION_V" + POSTFIX; + static { + superWordNodes(UMAX_REDUCTION_V, "UMaxReductionV"); + } + public static final String NEG_VF = VECTOR_PREFIX + "NEG_VF" + POSTFIX; static { vectorNode(NEG_VF, "NegVF", TYPE_FLOAT); diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorUMinMaxReductionTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorUMinMaxReductionTest.java new file mode 100644 index 00000000000..1c8cc34170e --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorUMinMaxReductionTest.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please 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.vectorapi; + +import compiler.lib.generators.*; +import compiler.lib.ir_framework.*; + +import jdk.incubator.vector.*; +import jdk.test.lib.Asserts; + +import java.util.function.BinaryOperator; + +/** + * @test + * @bug 8372980 + * @key randomness + * @summary Add intrinsic support for unsigned min/max reduction operations + * @library /test/lib / + * @modules jdk.incubator.vector + * @run driver compiler.vectorapi.VectorUMinMaxReductionTest + */ + +public class VectorUMinMaxReductionTest { + private static final VectorSpecies B_SPECIES = ByteVector.SPECIES_MAX; + private static final VectorSpecies S_SPECIES = ShortVector.SPECIES_MAX; + private static final VectorSpecies I_SPECIES = IntVector.SPECIES_MAX; + private static final VectorSpecies F_SPECIES = FloatVector.SPECIES_MAX; + private static final VectorSpecies L_SPECIES = LongVector.SPECIES_MAX; + private static final VectorSpecies D_SPECIES = DoubleVector.SPECIES_MAX; + private static final int LENGTH = 256; + private static final Generators RD = Generators.G; + + // Identity values for unsigned min/max operations + private static final byte BYTE_UMIN_IDENTITY = (byte) -1; + private static final byte BYTE_UMAX_IDENTITY = (byte) 0; + private static final short SHORT_UMIN_IDENTITY = (short) -1; + private static final short SHORT_UMAX_IDENTITY = (short) 0; + private static final int INT_UMIN_IDENTITY = -1; + private static final int INT_UMAX_IDENTITY = 0; + private static final long LONG_UMIN_IDENTITY = -1L; + private static final long LONG_UMAX_IDENTITY = 0L; + + private static byte[] ba; + private static short[] sa; + private static int[] ia; + private static long[] la; + private static boolean[] ma; + + static { + ba = new byte[LENGTH]; + sa = new short[LENGTH]; + ia = new int[LENGTH]; + la = new long[LENGTH]; + ma = new boolean[LENGTH]; + + Generator iGen = RD.ints(); + Generator lGen = RD.longs(); + + for (int i = 0; i < LENGTH; i++) { + ba[i] = iGen.next().byteValue(); + sa[i] = iGen.next().shortValue(); + ma[i] = iGen.next() % 2 == 0; + } + RD.fill(iGen, ia); + RD.fill(lGen, la); + } + + // ==================== Helper Functions ==================== + + @DontInline + private static void verifyByte(VectorSpecies species, byte got, byte init, + BinaryOperator op, boolean isMasked) { + byte expected = init; + for (int i = 0; i < species.length(); i++) { + if (!isMasked || ma[i]) { + expected = op.apply(expected, ba[i]); + } + } + Asserts.assertEquals(expected, got); + } + + @DontInline + private static void verifyShort(VectorSpecies species, short got, short init, + BinaryOperator op, boolean isMasked) { + short expected = init; + for (int i = 0; i < species.length(); i++) { + if (!isMasked || ma[i]) { + expected = op.apply(expected, sa[i]); + } + } + Asserts.assertEquals(expected, got); + } + + @DontInline + private static void verifyInt(VectorSpecies species, int got, int init, + BinaryOperator op, boolean isMasked) { + int expected = init; + for (int i = 0; i < species.length(); i++) { + if (!isMasked || ma[i]) { + expected = op.apply(expected, ia[i]); + } + } + Asserts.assertEquals(expected, got); + } + + @DontInline + private static void verifyLong(VectorSpecies species, long got, long init, + BinaryOperator op, boolean isMasked) { + long expected = init; + for (int i = 0; i < species.length(); i++) { + if (!isMasked || ma[i]) { + expected = op.apply(expected, la[i]); + } + } + Asserts.assertEquals(expected, got); + } + + // ==================== Byte Tests ==================== + + @Test + @IR(counts = {IRNode.UMIN_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testByteUMin() { + byte got = ByteVector.fromArray(B_SPECIES, ba, 0).reduceLanes(VectorOperators.UMIN); + verifyByte(B_SPECIES, got, BYTE_UMIN_IDENTITY, VectorMath::minUnsigned, false); + } + + @Test + @IR(counts = {IRNode.UMAX_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testByteUMax() { + byte got = ByteVector.fromArray(B_SPECIES, ba, 0).reduceLanes(VectorOperators.UMAX); + verifyByte(B_SPECIES, got, BYTE_UMAX_IDENTITY, VectorMath::maxUnsigned, false); + } + + @Test + @IR(counts = {IRNode.UMIN_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testByteUMinMasked() { + byte got = ByteVector.fromArray(B_SPECIES, ba, 0) + .reduceLanes(VectorOperators.UMIN, + VectorMask.fromArray(B_SPECIES, ma, 0)); + verifyByte(B_SPECIES, got, BYTE_UMIN_IDENTITY, VectorMath::minUnsigned, true); + } + + @Test + @IR(counts = {IRNode.UMAX_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testByteUMaxMasked() { + byte got = ByteVector.fromArray(B_SPECIES, ba, 0) + .reduceLanes(VectorOperators.UMAX, + VectorMask.fromArray(B_SPECIES, ma, 0)); + verifyByte(B_SPECIES, got, BYTE_UMAX_IDENTITY, VectorMath::maxUnsigned, true); + } + + // ==================== Short Tests ==================== + + @Test + @IR(counts = {IRNode.UMIN_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testShortUMin() { + short got = ShortVector.fromArray(S_SPECIES, sa, 0).reduceLanes(VectorOperators.UMIN); + verifyShort(S_SPECIES, got, SHORT_UMIN_IDENTITY, VectorMath::minUnsigned, false); + } + + @Test + @IR(counts = {IRNode.UMAX_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testShortUMax() { + short got = ShortVector.fromArray(S_SPECIES, sa, 0).reduceLanes(VectorOperators.UMAX); + verifyShort(S_SPECIES, got, SHORT_UMAX_IDENTITY, VectorMath::maxUnsigned, false); + } + + @Test + @IR(counts = {IRNode.UMIN_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testShortUMinMasked() { + short got = ShortVector.fromArray(S_SPECIES, sa, 0) + .reduceLanes(VectorOperators.UMIN, + VectorMask.fromArray(S_SPECIES, ma, 0)); + verifyShort(S_SPECIES, got, SHORT_UMIN_IDENTITY, VectorMath::minUnsigned, true); + } + + @Test + @IR(counts = {IRNode.UMAX_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testShortUMaxMasked() { + short got = ShortVector.fromArray(S_SPECIES, sa, 0) + .reduceLanes(VectorOperators.UMAX, + VectorMask.fromArray(S_SPECIES, ma, 0)); + verifyShort(S_SPECIES, got, SHORT_UMAX_IDENTITY, VectorMath::maxUnsigned, true); + } + + // ==================== Int Tests ==================== + + @Test + @IR(counts = {IRNode.UMIN_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testIntUMin() { + int got = IntVector.fromArray(I_SPECIES, ia, 0).reduceLanes(VectorOperators.UMIN); + verifyInt(I_SPECIES, got, INT_UMIN_IDENTITY, VectorMath::minUnsigned, false); + } + + @Test + @IR(counts = {IRNode.UMAX_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testIntUMax() { + int got = IntVector.fromArray(I_SPECIES, ia, 0).reduceLanes(VectorOperators.UMAX); + verifyInt(I_SPECIES, got, INT_UMAX_IDENTITY, VectorMath::maxUnsigned, false); + } + + @Test + @IR(counts = {IRNode.UMIN_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testIntUMinMasked() { + int got = IntVector.fromArray(I_SPECIES, ia, 0) + .reduceLanes(VectorOperators.UMIN, + VectorMask.fromArray(I_SPECIES, ma, 0)); + verifyInt(I_SPECIES, got, INT_UMIN_IDENTITY, VectorMath::minUnsigned, true); + } + + @Test + @IR(counts = {IRNode.UMAX_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testIntUMaxMasked() { + int got = IntVector.fromArray(I_SPECIES, ia, 0) + .reduceLanes(VectorOperators.UMAX, + VectorMask.fromArray(I_SPECIES, ma, 0)); + verifyInt(I_SPECIES, got, INT_UMAX_IDENTITY, VectorMath::maxUnsigned, true); + } + + // ==================== Long Tests ==================== + + @Test + @IR(counts = {IRNode.UMIN_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testLongUMin() { + long got = LongVector.fromArray(L_SPECIES, la, 0).reduceLanes(VectorOperators.UMIN); + verifyLong(L_SPECIES, got, LONG_UMIN_IDENTITY, VectorMath::minUnsigned, false); + } + + @Test + @IR(counts = {IRNode.UMAX_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testLongUMax() { + long got = LongVector.fromArray(L_SPECIES, la, 0).reduceLanes(VectorOperators.UMAX); + verifyLong(L_SPECIES, got, LONG_UMAX_IDENTITY, VectorMath::maxUnsigned, false); + } + + @Test + @IR(counts = {IRNode.UMIN_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testLongUMinMasked() { + long got = LongVector.fromArray(L_SPECIES, la, 0) + .reduceLanes(VectorOperators.UMIN, + VectorMask.fromArray(L_SPECIES, ma, 0)); + verifyLong(L_SPECIES, got, LONG_UMIN_IDENTITY, VectorMath::minUnsigned, true); + } + + @Test + @IR(counts = {IRNode.UMAX_REDUCTION_V, "= 1"}, + applyIfCPUFeature = {"asimd", "true"}) + public static void testLongUMaxMasked() { + long got = LongVector.fromArray(L_SPECIES, la, 0) + .reduceLanes(VectorOperators.UMAX, + VectorMask.fromArray(L_SPECIES, ma, 0)); + verifyLong(L_SPECIES, got, LONG_UMAX_IDENTITY, VectorMath::maxUnsigned, true); + } + + public static void main(String[] args) { + TestFramework testFramework = new TestFramework(); + testFramework.setDefaultWarmup(10000) + .addFlags("--add-modules=jdk.incubator.vector") + .start(); + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorUMinUMaxReductionBenchmark.java b/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorUMinUMaxReductionBenchmark.java new file mode 100644 index 00000000000..658717184a0 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/vector/VectorUMinUMaxReductionBenchmark.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.jdk.incubator.vector; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.*; + +import jdk.incubator.vector.*; +import java.util.concurrent.TimeUnit; +import java.util.Random; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 2, jvmArgs = { "--add-modules=jdk.incubator.vector" }) +public class VectorUMinUMaxReductionBenchmark { + @Param({"1024"}) + private int size; + + private static final VectorSpecies B_SPECIES = VectorSpecies.ofLargestShape(byte.class); + private static final VectorSpecies S_SPECIES = VectorSpecies.ofLargestShape(short.class); + private static final VectorSpecies I_SPECIES = VectorSpecies.ofLargestShape(int.class); + private static final VectorSpecies L_SPECIES = VectorSpecies.ofLargestShape(long.class); + + private Random r = new Random(); + private byte[] ba; + private short[] sa; + private int[] ia; + private long[] la; + private boolean[] ma; + + @Setup + public void init() { + ba = new byte[size]; + sa = new short[size]; + ia = new int[size]; + la = new long[size]; + ma = new boolean[size]; + + for (int i = 0; i < size; i++) { + ba[i] = (byte) r.nextInt(); + sa[i] = (short) r.nextInt(); + ia[i] = r.nextInt(); + la[i] = r.nextLong(); + ma[i] = r.nextInt() % 2 == 0; + } + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private byte byteUMinUMaxReductionKernel(VectorOperators.Associative op) { + byte res = 0; + for (int i = 0; i < B_SPECIES.loopBound(size); i += B_SPECIES.length()) { + ByteVector av = ByteVector.fromArray(B_SPECIES, ba, i); + res += av.reduceLanes(op); + } + return res; + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private byte byteUMinUMaxReductionMaskedKernel(VectorOperators.Associative op) { + byte res = 0; + VectorMask m = VectorMask.fromArray(B_SPECIES, ma, 0); + for (int i = 0; i < B_SPECIES.loopBound(size); i += B_SPECIES.length()) { + ByteVector av = ByteVector.fromArray(B_SPECIES, ba, i); + res += av.reduceLanes(op, m); + } + return res; + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private short shortUMinUMaxReductionKernel(VectorOperators.Associative op) { + short res = 0; + for (int i = 0; i < S_SPECIES.loopBound(size); i += S_SPECIES.length()) { + ShortVector av = ShortVector.fromArray(S_SPECIES, sa, i); + res += av.reduceLanes(op); + } + return res; + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private short shortUMinUMaxReductionMaskedKernel(VectorOperators.Associative op) { + short res = 0; + VectorMask m = VectorMask.fromArray(S_SPECIES, ma, 0); + for (int i = 0; i < S_SPECIES.loopBound(size); i += S_SPECIES.length()) { + ShortVector av = ShortVector.fromArray(S_SPECIES, sa, i); + res += av.reduceLanes(op, m); + } + return res; + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private int intUMinUMaxReductionKernel(VectorOperators.Associative op) { + int res = 0; + for (int i = 0; i < I_SPECIES.loopBound(size); i += I_SPECIES.length()) { + IntVector av = IntVector.fromArray(I_SPECIES, ia, i); + res += av.reduceLanes(op); + } + return res; + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private int intUMinUMaxReductionMaskedKernel(VectorOperators.Associative op) { + int res = 0; + VectorMask m = VectorMask.fromArray(I_SPECIES, ma, 0); + for (int i = 0; i < I_SPECIES.loopBound(size); i += I_SPECIES.length()) { + IntVector av = IntVector.fromArray(I_SPECIES, ia, i); + res += av.reduceLanes(op, m); + } + return res; + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private long longUMinUMaxReductionKernel(VectorOperators.Associative op) { + long res = 0L; + for (int i = 0; i < L_SPECIES.loopBound(size); i += L_SPECIES.length()) { + LongVector av = LongVector.fromArray(L_SPECIES, la, i); + res += av.reduceLanes(op); + } + return res; + } + + @CompilerControl(CompilerControl.Mode.INLINE) + private long longUMinUMaxReductionMaskedKernel(VectorOperators.Associative op) { + long res = 0L; + VectorMask m = VectorMask.fromArray(L_SPECIES, ma, 0); + for (int i = 0; i < L_SPECIES.loopBound(size); i += L_SPECIES.length()) { + LongVector av = LongVector.fromArray(L_SPECIES, la, i); + res += av.reduceLanes(op, m); + } + return res; + } + + @Benchmark + public byte byteUMinReduction() { + return byteUMinUMaxReductionKernel(VectorOperators.UMIN); + } + + @Benchmark + public byte byteUMaxReduction() { + return byteUMinUMaxReductionKernel(VectorOperators.UMAX); + } + + @Benchmark + public byte byteUMinReductionMasked() { + return byteUMinUMaxReductionMaskedKernel(VectorOperators.UMIN); + } + + @Benchmark + public byte byteUMaxReductionMasked() { + return byteUMinUMaxReductionMaskedKernel(VectorOperators.UMAX); + } + + @Benchmark + public short shortUMinReduction() { + return shortUMinUMaxReductionKernel(VectorOperators.UMIN); + } + + @Benchmark + public short shortUMaxReduction() { + return shortUMinUMaxReductionKernel(VectorOperators.UMAX); + } + + @Benchmark + public short shortUMinReductionMasked() { + return shortUMinUMaxReductionMaskedKernel(VectorOperators.UMIN); + } + + @Benchmark + public short shortUMaxReductionMasked() { + return shortUMinUMaxReductionMaskedKernel(VectorOperators.UMAX); + } + + @Benchmark + public int intUMinReduction() { + return intUMinUMaxReductionKernel(VectorOperators.UMIN); + } + + @Benchmark + public int intUMaxReduction() { + return intUMinUMaxReductionKernel(VectorOperators.UMAX); + } + + @Benchmark + public int intUMinReductionMasked() { + return intUMinUMaxReductionMaskedKernel(VectorOperators.UMIN); + } + + @Benchmark + public int intUMaxReductionMasked() { + return intUMinUMaxReductionMaskedKernel(VectorOperators.UMAX); + } + + @Benchmark + public long longUMinReduction() { + return longUMinUMaxReductionKernel(VectorOperators.UMIN); + } + + @Benchmark + public long longUMaxReduction() { + return longUMinUMaxReductionKernel(VectorOperators.UMAX); + } + + @Benchmark + public long longUMinReductionMasked() { + return longUMinUMaxReductionMaskedKernel(VectorOperators.UMIN); + } + + @Benchmark + public long longUMaxReductionMasked() { + return longUMinUMaxReductionMaskedKernel(VectorOperators.UMAX); + } +} \ No newline at end of file From fde77a9b55828128eae9652726638a14ce426e09 Mon Sep 17 00:00:00 2001 From: Mikhail Yankelevich Date: Thu, 5 Feb 2026 10:09:25 +0000 Subject: [PATCH 130/215] 8365883: Fix P11Cipher to throw BadPaddingException for PKCS11 CKR_ENCRYPTED_DATA_INVALID error Reviewed-by: valeriep --- .../sun/security/pkcs11/P11Cipher.java | 9 ++++- .../pkcs11/Cipher/TestPKCS5PaddingError.java | 38 +++++++++++++------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java index 9c923b76215..39827af42e5 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11Cipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -1198,7 +1198,9 @@ final class P11Cipher extends CipherSpi { } private void handleException(PKCS11Exception e) - throws ShortBufferException, IllegalBlockSizeException { + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + if (e.match(CKR_BUFFER_TOO_SMALL)) { throw (ShortBufferException) (new ShortBufferException().initCause(e)); @@ -1206,6 +1208,9 @@ final class P11Cipher extends CipherSpi { e.match(CKR_ENCRYPTED_DATA_LEN_RANGE)) { throw (IllegalBlockSizeException) (new IllegalBlockSizeException(e.toString()).initCause(e)); + } else if (e.match(CKR_ENCRYPTED_DATA_INVALID)) { + throw (BadPaddingException) + (new BadPaddingException(e.toString()).initCause(e)); } } diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java b/test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java index a8f50dddb53..a6448b2783e 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java +++ b/test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.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 @@ -34,6 +34,7 @@ import java.security.AlgorithmParameters; import java.security.NoSuchAlgorithmException; import java.security.Provider; +import java.security.Security; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; @@ -63,40 +64,55 @@ public class TestPKCS5PaddingError extends PKCS11Test { }; private static StringBuffer debugBuf = new StringBuffer(); + private static final String sunJCEProvider = + System.getProperty("test.provider.name", "SunJCE"); @Override public void main(Provider p) throws Exception { + + // Checking for SunJCE first + System.out.println("Checking " + sunJCEProvider + " provider"); + doTest(Security.getProvider(sunJCEProvider)); + + System.out.printf("Checking %s provider%n", p.getName()); + doTest(p); + } + + private void doTest(final Provider p) throws Exception { try { byte[] plainText = new byte[200]; - for (int i = 0; i < TEST_LIST.length; i++) { - CI currTest = TEST_LIST[i]; + for (CI currTest : TEST_LIST) { System.out.println("===" + currTest.transformation + "==="); try { KeyGenerator kg = KeyGenerator.getInstance(currTest.keyAlgo, p); SecretKey key = kg.generateKey(); Cipher c1 = Cipher.getInstance(currTest.transformation, - System.getProperty("test.provider.name", "SunJCE")); + sunJCEProvider); c1.init(Cipher.ENCRYPT_MODE, key); byte[] cipherText = c1.doFinal(plainText); AlgorithmParameters params = c1.getParameters(); Cipher c2 = Cipher.getInstance(currTest.transformation, p); c2.init(Cipher.DECRYPT_MODE, key, params); + c2.doFinal(cipherText); // 1st test: wrong output length - // NOTE: Skip NSS since it reports CKR_DEVICE_ERROR when - // the data passed to its EncryptUpdate/DecryptUpdate is - // not multiple of blocks + // NOTE: Skip NSS since it reports CKR_DEVICE_ERROR when the + // data passed to its EncryptUpdate and + // CKR_ENCRYPTED_DATA_LEN_RANGE when passed through + // DecryptUpdate are not multiple of blocks if (!p.getName().equals("SunPKCS11-NSS")) { try { System.out.println("Testing with wrong cipherText length"); c2.doFinal(cipherText, 0, cipherText.length - 2); + throw new RuntimeException( + "Expected IBSE NOT thrown"); } catch (IllegalBlockSizeException ibe) { // expected } catch (Exception ex) { System.out.println("Error: Unexpected Ex " + ex); - ex.printStackTrace(); + throw ex; } } // 2nd test: wrong padding value @@ -104,11 +120,12 @@ public class TestPKCS5PaddingError extends PKCS11Test { System.out.println("Testing with wrong padding bytes"); cipherText[cipherText.length - 1]++; c2.doFinal(cipherText); + throw new RuntimeException("Expected BPE NOT thrown"); } catch (BadPaddingException bpe) { // expected } catch (Exception ex) { System.out.println("Error: Unexpected Ex " + ex); - ex.printStackTrace(); + throw ex; } System.out.println("DONE"); } catch (NoSuchAlgorithmException nsae) { @@ -119,13 +136,12 @@ public class TestPKCS5PaddingError extends PKCS11Test { } catch (Exception ex) { // print out debug info when exception is encountered if (debugBuf != null) { - System.out.println(debugBuf.toString()); + System.out.println(debugBuf); debugBuf = new StringBuffer(); } throw ex; } } - public static void main(String[] args) throws Exception { main(new TestPKCS5PaddingError(), args); } From 72eec521f91d34b97dabfc3ee8cea511d88079e2 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 5 Feb 2026 11:50:14 +0000 Subject: [PATCH 131/215] 8377161: G1: Remove unnecessary member G1FullGCScope::_g1h Reviewed-by: shade, ayang --- src/hotspot/share/gc/g1/g1FullGCScope.cpp | 5 ++--- src/hotspot/share/gc/g1/g1FullGCScope.hpp | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.cpp b/src/hotspot/share/gc/g1/g1FullGCScope.cpp index 8b92d51a8a3..083b77b44b7 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.cpp @@ -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 @@ -42,14 +42,13 @@ G1FullGCScope::G1FullGCScope(G1MonitoringSupport* monitoring_support, _rm(), _should_clear_soft_refs(clear_soft), _do_maximal_compaction(do_maximal_compaction), - _g1h(G1CollectedHeap::heap()), _svc_marker(SvcGCMarker::FULL), _timer(), _tracer(tracer), _active(), _tracer_mark(&_timer, _tracer), _monitoring_scope(monitoring_support), - _heap_printer(_g1h), + _heap_printer(G1CollectedHeap::heap()), _region_compaction_threshold(do_maximal_compaction ? G1HeapRegion::GrainWords : (1 - MarkSweepDeadRatio / 100.0) * G1HeapRegion::GrainWords) { } diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.hpp b/src/hotspot/share/gc/g1/g1FullGCScope.hpp index 7a0d0a5395e..278a00cedbd 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.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 @@ -49,7 +49,6 @@ class G1FullGCScope : public StackObj { ResourceMark _rm; bool _should_clear_soft_refs; bool _do_maximal_compaction; - G1CollectedHeap* _g1h; SvcGCMarker _svc_marker; STWGCTimer _timer; G1FullGCTracer* _tracer; From d93bd18d67555ba998735196576c337249f4932b Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Thu, 5 Feb 2026 11:55:02 +0000 Subject: [PATCH 132/215] 8377167: javax/imageio/ReadAbortTest.java throw NPE when x11 unavailable Reviewed-by: prr, serb --- test/jdk/javax/imageio/ReadAbortTest.java | 11 +++++++---- test/jdk/javax/imageio/WriteAbortTest.java | 15 +++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/test/jdk/javax/imageio/ReadAbortTest.java b/test/jdk/javax/imageio/ReadAbortTest.java index 7476dbe0de8..bda17aa4204 100644 --- a/test/jdk/javax/imageio/ReadAbortTest.java +++ b/test/jdk/javax/imageio/ReadAbortTest.java @@ -30,16 +30,17 @@ * calling IIOReadProgressListener.readAborted() for all readers. * @run main ReadAbortTest */ +import java.awt.Color; +import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; +import java.nio.file.Files; import java.util.Iterator; + import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.event.IIOReadProgressListener; import javax.imageio.stream.ImageInputStream; -import java.awt.Color; -import java.awt.Graphics2D; -import java.nio.file.Files; public class ReadAbortTest implements IIOReadProgressListener { @@ -103,7 +104,9 @@ public class ReadAbortTest implements IIOReadProgressListener { } catch (Exception e) { throw e; } finally { - Files.delete(file.toPath()); + if (file != null && file.exists()) { + Files.delete(file.toPath()); + } } } diff --git a/test/jdk/javax/imageio/WriteAbortTest.java b/test/jdk/javax/imageio/WriteAbortTest.java index 624ce16c94e..43abd703d7d 100644 --- a/test/jdk/javax/imageio/WriteAbortTest.java +++ b/test/jdk/javax/imageio/WriteAbortTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 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 @@ -30,15 +30,16 @@ * calling IIOWriteProgressListener.readAborted() for all readers. * @run main WriteAbortTest */ -import java.awt.image.BufferedImage; -import java.io.File; -import javax.imageio.ImageIO; -import javax.imageio.stream.ImageInputStream; import java.awt.Color; import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; import java.nio.file.Files; + +import javax.imageio.ImageIO; import javax.imageio.ImageWriter; import javax.imageio.event.IIOWriteProgressListener; +import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; public class WriteAbortTest implements IIOWriteProgressListener { @@ -98,7 +99,9 @@ public class WriteAbortTest implements IIOWriteProgressListener { + format); } } finally { - Files.delete(file.toPath()); + if (file != null && file.exists()) { + Files.delete(file.toPath()); + } } } From 1ac965893da6a9a3d220d572cab4ac6030ba1722 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 5 Feb 2026 12:16:11 +0000 Subject: [PATCH 133/215] 8376956: Add JVMTI phase entering/setting to hserr event log Reviewed-by: sspitsyn, lucy --- src/hotspot/share/prims/jvmtiExport.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index 02f39460ff6..fca348fda26 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -71,6 +71,7 @@ #include "runtime/threadSMR.hpp" #include "runtime/vframe.inline.hpp" #include "runtime/vm_version.hpp" +#include "utilities/events.hpp" #include "utilities/macros.hpp" #ifdef JVMTI_TRACE @@ -648,22 +649,27 @@ JvmtiExport::decode_version_values(jint version, int * major, int * minor, } void JvmtiExport::enter_primordial_phase() { + Events::log(Thread::current_or_null(), "JVMTI - enter primordial phase"); JvmtiEnvBase::set_phase(JVMTI_PHASE_PRIMORDIAL); } void JvmtiExport::enter_early_start_phase() { + Events::log(Thread::current_or_null(), "JVMTI - enter early start phase"); set_early_vmstart_recorded(true); } void JvmtiExport::enter_start_phase() { + Events::log(Thread::current_or_null(), "JVMTI - enter start phase"); JvmtiEnvBase::set_phase(JVMTI_PHASE_START); } void JvmtiExport::enter_onload_phase() { + Events::log(Thread::current_or_null(), "JVMTI - enter onload phase"); JvmtiEnvBase::set_phase(JVMTI_PHASE_ONLOAD); } void JvmtiExport::enter_live_phase() { + Events::log(Thread::current_or_null(), "JVMTI - enter live phase"); JvmtiEnvBase::set_phase(JVMTI_PHASE_LIVE); } @@ -813,6 +819,7 @@ void JvmtiExport::post_vm_death() { } } + Events::log(Thread::current_or_null(), "JVMTI - enter dead phase"); JvmtiEnvBase::set_phase(JVMTI_PHASE_DEAD); } From 37b49b29a724c2c3c30890ba7a46c4aece71a0fe Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Thu, 5 Feb 2026 12:43:32 +0000 Subject: [PATCH 134/215] 8377138: VMError::report should handle out-of-stackbounds errors gracefully Reviewed-by: lucy, stefank, mdoerr --- src/hotspot/share/utilities/vmError.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp index 88f81b31293..6088eafbda4 100644 --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -966,10 +966,15 @@ void VMError::report(outputStream* st, bool _verbose) { frame fr = _context ? os::fetch_frame_from_context(_context) : os::current_frame(); - if (fr.sp()) { - st->print(", sp=" PTR_FORMAT, p2i(fr.sp())); - size_t free_stack_size = pointer_delta(fr.sp(), stack_bottom, 1024); - st->print(", free space=%zuk", free_stack_size); + address sp = (address)fr.sp(); + if (sp != nullptr) { + st->print(", sp=" PTR_FORMAT, p2i(sp)); + if (sp >= stack_bottom && sp < stack_top) { + size_t free_stack_size = pointer_delta(sp, stack_bottom, 1024); + st->print(", free space=%zuk", free_stack_size); + } else { + st->print(" **OUTSIDE STACK**."); + } } st->cr(); From ac6e8d481a20b40e136263c0e0c075bd138677ab Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Thu, 5 Feb 2026 13:46:23 +0000 Subject: [PATCH 135/215] 8376568: Change Thread::getStackTrace to use handshake op for all cases Reviewed-by: pchilanomate, sspitsyn --- src/hotspot/share/classfile/javaClasses.cpp | 60 +++------ src/hotspot/share/classfile/javaClasses.hpp | 3 - src/hotspot/share/classfile/vmSymbols.hpp | 4 +- src/hotspot/share/include/jvm.h | 4 +- src/hotspot/share/prims/jvm.cpp | 8 +- src/hotspot/share/services/threadService.cpp | 31 ++--- src/hotspot/share/services/threadService.hpp | 4 +- .../classes/java/lang/StackTraceElement.java | 6 +- .../share/classes/java/lang/System.java | 8 +- .../share/classes/java/lang/Thread.java | 33 ++--- .../classes/java/lang/VirtualThread.java | 126 +++--------------- .../jdk/internal/access/JavaLangAccess.java | 7 +- .../jdk/internal/vm/ThreadSnapshot.java | 17 ++- src/java.base/share/native/libjava/Thread.c | 4 +- .../vm/ThreadSnapshot/ThreadNotAlive.java | 65 +++++++++ .../java.base/jdk/internal/vm/Helper.java | 38 ++++++ .../lang/ThreadGetStackTraceWhenParked.java | 63 +++++++++ .../lang/ThreadGetStackTraceWhenSpinning.java | 61 +++++++++ ...irtualThreadGetStackTraceWhenSpinning.java | 61 +++++++++ ...rtualThreadGetStackTraceWhenUnmounted.java | 63 +++++++++ ...irtualThreadGetStackTraceWhenYielding.java | 61 +++++++++ 21 files changed, 502 insertions(+), 225 deletions(-) create mode 100644 test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadNotAlive.java create mode 100644 test/jdk/jdk/internal/vm/ThreadSnapshot/java.base/jdk/internal/vm/Helper.java create mode 100644 test/micro/org/openjdk/bench/java/lang/ThreadGetStackTraceWhenParked.java create mode 100644 test/micro/org/openjdk/bench/java/lang/ThreadGetStackTraceWhenSpinning.java create mode 100644 test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenSpinning.java create mode 100644 test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenUnmounted.java create mode 100644 test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenYielding.java diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index b650bf8cfb8..c6b0fcb90e0 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1908,16 +1908,16 @@ oop java_lang_Thread::park_blocker(oop java_thread) { return java_thread->obj_field_access(_park_blocker_offset); } -// Obtain stack trace for platform or mounted virtual thread. -// If jthread is a virtual thread and it has been unmounted (or remounted to different carrier) the method returns null. -// The caller (java.lang.VirtualThread) handles returned nulls via retry. +// Obtain stack trace for a platform or virtual thread. oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { ThreadsListHandle tlh(THREAD); JavaThread* java_thread = nullptr; - oop thread_oop; + oop thread_oop = nullptr; bool has_java_thread = tlh.cv_internal_thread_to_JavaThread(jthread, &java_thread, &thread_oop); - if (!has_java_thread) { + assert(thread_oop != nullptr, "Missing Thread oop"); + bool is_virtual = java_lang_VirtualThread::is_instance(thread_oop); + if (!has_java_thread && !is_virtual) { return nullptr; } @@ -1925,12 +1925,11 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { public: const Handle _thread_h; int _depth; - bool _retry_handshake; GrowableArray* _methods; GrowableArray* _bcis; GetStackTraceHandshakeClosure(Handle thread_h) : - HandshakeClosure("GetStackTraceHandshakeClosure"), _thread_h(thread_h), _depth(0), _retry_handshake(false), + HandshakeClosure("GetStackTraceHandshakeClosure"), _thread_h(thread_h), _depth(0), _methods(nullptr), _bcis(nullptr) { } ~GetStackTraceHandshakeClosure() { @@ -1938,37 +1937,15 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { delete _bcis; } - bool read_reset_retry() { - bool ret = _retry_handshake; - // If we re-execute the handshake this method need to return false - // when the handshake cannot be performed. (E.g. thread terminating) - _retry_handshake = false; - return ret; - } - void do_thread(Thread* th) { - if (!Thread::current()->is_Java_thread()) { - _retry_handshake = true; + JavaThread* java_thread = th != nullptr ? JavaThread::cast(th) : nullptr; + if (java_thread != nullptr && !java_thread->has_last_Java_frame()) { + // stack trace is empty return; } - JavaThread* java_thread = JavaThread::cast(th); - - if (!java_thread->has_last_Java_frame()) { - return; - } - - bool carrier = false; - if (java_lang_VirtualThread::is_instance(_thread_h())) { - // Ensure _thread_h is still mounted to java_thread. - const ContinuationEntry* ce = java_thread->vthread_continuation(); - if (ce == nullptr || ce->cont_oop(java_thread) != java_lang_VirtualThread::continuation(_thread_h())) { - // Target thread has been unmounted. - return; - } - } else { - carrier = (java_thread->vthread_continuation() != nullptr); - } + bool is_virtual = java_lang_VirtualThread::is_instance(_thread_h()); + bool vthread_carrier = !is_virtual && (java_thread->vthread_continuation() != nullptr); const int max_depth = MaxJavaStackTraceDepth; const bool skip_hidden = !ShowHiddenFrames; @@ -1979,7 +1956,10 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { _bcis = new (mtInternal) GrowableArray(init_length, mtInternal); int total_count = 0; - for (vframeStream vfst(java_thread, false, false, carrier); // we don't process frames as we don't care about oops + vframeStream vfst(java_thread != nullptr + ? vframeStream(java_thread, false, false, vthread_carrier) // we don't process frames as we don't care about oops + : vframeStream(java_lang_VirtualThread::continuation(_thread_h()))); + for (; !vfst.at_end() && (max_depth == 0 || max_depth != total_count); vfst.next()) { @@ -2001,9 +1981,11 @@ oop java_lang_Thread::async_get_stack_trace(jobject jthread, TRAPS) { ResourceMark rm(THREAD); HandleMark hm(THREAD); GetStackTraceHandshakeClosure gsthc(Handle(THREAD, thread_oop)); - do { - Handshake::execute(&gsthc, &tlh, java_thread); - } while (gsthc.read_reset_retry()); + if (is_virtual) { + Handshake::execute(&gsthc, thread_oop); + } else { + Handshake::execute(&gsthc, &tlh, java_thread); + } // Stop if no stack trace is found. if (gsthc._depth == 0) { @@ -2200,7 +2182,7 @@ void java_lang_VirtualThread::set_timeout(oop vthread, jlong value) { JavaThreadStatus java_lang_VirtualThread::map_state_to_thread_status(int state) { JavaThreadStatus status = JavaThreadStatus::NEW; - switch (state & ~SUSPENDED) { + switch (state) { case NEW: status = JavaThreadStatus::NEW; break; diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index a8562a345c8..3276d398faf 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -592,9 +592,6 @@ class java_lang_VirtualThread : AllStatic { TIMED_WAITING = 17, TIMED_WAIT = 18, // waiting in timed-Object.wait TERMINATED = 99, - - // additional state bits - SUSPENDED = 1 << 8, // suspended when unmounted }; static void compute_offsets(); diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index 79646f24d0e..0054b7ba3f2 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.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 @@ -748,8 +748,6 @@ class SerializeClosure; template(jdk_internal_vm_ThreadLock, "jdk/internal/vm/ThreadSnapshot$ThreadLock") \ template(jdk_internal_vm_ThreadLock_signature, "Ljdk/internal/vm/ThreadSnapshot$ThreadLock;") \ template(jdk_internal_vm_ThreadLock_array, "[Ljdk/internal/vm/ThreadSnapshot$ThreadLock;") \ - template(java_lang_StackTraceElement_of_name, "of") \ - template(java_lang_StackTraceElement_of_signature, "([Ljava/lang/StackTraceElement;)[Ljava/lang/StackTraceElement;") \ \ /* jcmd Thread.vthread_scheduler and Thread.vthread_pollers */ \ template(jdk_internal_vm_JcmdVThreadCommands, "jdk/internal/vm/JcmdVThreadCommands") \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index 7236679d8f3..f99e8f162c6 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -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 @@ -301,7 +301,7 @@ JVM_Interrupt(JNIEnv *env, jobject thread); JNIEXPORT jboolean JNICALL JVM_HoldsLock(JNIEnv *env, jclass threadClass, jobject obj); -JNIEXPORT jobject JNICALL +JNIEXPORT jobjectArray JNICALL JVM_GetStackTrace(JNIEnv *env, jobject thread); JNIEXPORT jobject JNICALL diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 6d6937a97e9..2b3a2966c27 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2910,18 +2910,14 @@ JVM_ENTRY(jboolean, JVM_HoldsLock(JNIEnv* env, jclass threadClass, jobject obj)) return ObjectSynchronizer::current_thread_holds_lock(thread, h_obj); JVM_END -JVM_ENTRY(jobject, JVM_GetStackTrace(JNIEnv *env, jobject jthread)) +JVM_ENTRY(jobjectArray, JVM_GetStackTrace(JNIEnv *env, jobject jthread)) oop trace = java_lang_Thread::async_get_stack_trace(jthread, THREAD); - return JNIHandles::make_local(THREAD, trace); + return (jobjectArray) JNIHandles::make_local(THREAD, trace); JVM_END JVM_ENTRY(jobject, JVM_CreateThreadSnapshot(JNIEnv* env, jobject jthread)) -#if INCLUDE_JVMTI oop snapshot = ThreadSnapshotFactory::get_thread_snapshot(jthread, THREAD); return JNIHandles::make_local(THREAD, snapshot); -#else - THROW_NULL(vmSymbols::java_lang_UnsupportedOperationException()); -#endif JVM_END JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name)) diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index 8772195df0b..4dc6abe54b7 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -44,7 +44,6 @@ #include "runtime/atomicAccess.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" -#include "runtime/javaCalls.hpp" #include "runtime/javaThread.inline.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/objectMonitor.inline.hpp" @@ -1122,8 +1121,6 @@ ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread, // jdk.internal.vm.ThreadSnapshot support -#if INCLUDE_JVMTI - class GetThreadSnapshotHandshakeClosure: public HandshakeClosure { private: static OopStorage* oop_storage() { @@ -1300,7 +1297,7 @@ public: return; } - bool vthread_carrier = !is_virtual && (_java_thread != nullptr) && (_java_thread->vthread_continuation() != nullptr); + bool vthread_carrier = !is_virtual && (_java_thread->vthread_continuation() != nullptr); oop park_blocker = java_lang_Thread::park_blocker(_thread_h()); if (park_blocker != nullptr) { @@ -1458,13 +1455,13 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { HandleMark hm(THREAD); JavaThread* java_thread = nullptr; - oop thread_oop; + oop thread_oop = nullptr; bool has_javathread = tlh.cv_internal_thread_to_JavaThread(jthread, &java_thread, &thread_oop); - assert((has_javathread && thread_oop != nullptr) || !has_javathread, "Missing Thread oop"); + assert(thread_oop != nullptr, "Missing Thread oop"); bool is_virtual = java_lang_VirtualThread::is_instance(thread_oop); // Deals with null if (!has_javathread && !is_virtual) { - return nullptr; // thread terminated so not of interest + return nullptr; // platform thread terminated } // Handshake with target @@ -1476,6 +1473,11 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { Handshake::execute(&cl, &tlh, java_thread); } + assert(cl._thread_status != JavaThreadStatus::NEW, "unstarted Thread"); + if (is_virtual && (cl._thread_status == JavaThreadStatus::TERMINATED)) { + return nullptr; // virtual thread terminated + } + // StackTrace InstanceKlass* ste_klass = vmClasses::StackTraceElement_klass(); assert(ste_klass != nullptr, "must be loaded"); @@ -1505,17 +1507,6 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { } } - // call static StackTraceElement[] StackTraceElement.of(StackTraceElement[] stackTrace) - // to properly initialize STEs. - JavaValue result(T_OBJECT); - JavaCalls::call_static(&result, - ste_klass, - vmSymbols::java_lang_StackTraceElement_of_name(), - vmSymbols::java_lang_StackTraceElement_of_signature(), - trace, - CHECK_NULL); - // the method return the same trace array - Symbol* snapshot_klass_name = vmSymbols::jdk_internal_vm_ThreadSnapshot(); Klass* snapshot_klass = SystemDictionary::resolve_or_fail(snapshot_klass_name, true, CHECK_NULL); if (snapshot_klass->should_be_initialized()) { @@ -1534,5 +1525,3 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) { } return snapshot(); } - -#endif // INCLUDE_JVMTI diff --git a/src/hotspot/share/services/threadService.hpp b/src/hotspot/share/services/threadService.hpp index bbd0722c51f..4b0e76658b6 100644 --- a/src/hotspot/share/services/threadService.hpp +++ b/src/hotspot/share/services/threadService.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -635,7 +635,7 @@ class JavaThreadSleepState : public JavaThreadStatusChanger { // jdk.internal.vm.ThreadSnapshot support class ThreadSnapshotFactory: AllStatic { public: - JVMTI_ONLY(static oop get_thread_snapshot(jobject jthread, TRAPS);) + static oop get_thread_snapshot(jobject jthread, TRAPS); }; #endif // SHARE_SERVICES_THREADSERVICE_HPP diff --git a/src/java.base/share/classes/java/lang/StackTraceElement.java b/src/java.base/share/classes/java/lang/StackTraceElement.java index 70219a7f6ec..bbcfa2a8a17 100644 --- a/src/java.base/share/classes/java/lang/StackTraceElement.java +++ b/src/java.base/share/classes/java/lang/StackTraceElement.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 @@ -561,7 +561,7 @@ public final class StackTraceElement implements java.io.Serializable { // VM to fill in StackTraceElement initStackTraceElements(stackTrace, x, depth); - return of(stackTrace); + return finishInit(stackTrace); } /* @@ -575,7 +575,7 @@ public final class StackTraceElement implements java.io.Serializable { return ste; } - static StackTraceElement[] of(StackTraceElement[] stackTrace) { + static StackTraceElement[] finishInit(StackTraceElement[] stackTrace) { // ensure the proper StackTraceElement initialization for (StackTraceElement ste : stackTrace) { ste.computeFormat(); diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index cfe09c61375..831ce81c7c5 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -55,7 +55,6 @@ import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledExecutorService; import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -2339,6 +2338,11 @@ public final class System { public boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars) { return string.bytesCompatible(charset, srcIndex, numChars); } + + @Override + public void finishInit(StackTraceElement[] stackTrace) { + StackTraceElement.finishInit(stackTrace); + } }); } } diff --git a/src/java.base/share/classes/java/lang/Thread.java b/src/java.base/share/classes/java/lang/Thread.java index 798ded2b941..bb1292b374a 100644 --- a/src/java.base/share/classes/java/lang/Thread.java +++ b/src/java.base/share/classes/java/lang/Thread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -2208,40 +2208,23 @@ public class Thread implements Runnable { * @since 1.5 */ public StackTraceElement[] getStackTrace() { - if (this != Thread.currentThread()) { + if (Thread.currentThread() != this) { // optimization so we do not call into the vm for threads that // have not yet started or have terminated if (!isAlive()) { return EMPTY_STACK_TRACE; } - StackTraceElement[] stackTrace = asyncGetStackTrace(); - return (stackTrace != null) ? stackTrace : EMPTY_STACK_TRACE; + StackTraceElement[] stackTrace = getStackTrace0(); + if (stackTrace != null) { + return StackTraceElement.finishInit(stackTrace); + } + return EMPTY_STACK_TRACE; } else { return (new Exception()).getStackTrace(); } } - /** - * Returns an array of stack trace elements representing the stack dump of - * this thread. Returns null if the stack trace cannot be obtained. In - * the default implementation, null is returned if the thread is a virtual - * thread that is not mounted or the thread is a platform thread that has - * terminated. - */ - StackTraceElement[] asyncGetStackTrace() { - Object stackTrace = getStackTrace0(); - if (stackTrace == null) { - return null; - } - StackTraceElement[] stes = (StackTraceElement[]) stackTrace; - if (stes.length == 0) { - return null; - } else { - return StackTraceElement.of(stes); - } - } - - private native Object getStackTrace0(); + private native StackTraceElement[] getStackTrace0(); /** * Returns a map of stack traces for all live platform threads. The map diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index 5526618c67b..6cd7ccbbba1 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -154,9 +154,6 @@ final class VirtualThread extends BaseVirtualThread { private static final int TERMINATED = 99; // final state - // can be suspended from scheduling when unmounted - private static final int SUSPENDED = 1 << 8; - // parking permit made available by LockSupport.unpark private volatile boolean parkPermit; @@ -495,7 +492,7 @@ final class VirtualThread extends BaseVirtualThread { @ChangesCurrentThread @ReservedStackAccess private void mount() { - startTransition(/*is_mount*/true); + startTransition(/*mount*/true); // We assume following volatile accesses provide equivalent // of acquire ordering, otherwise we need U.loadFence() here. @@ -540,7 +537,7 @@ final class VirtualThread extends BaseVirtualThread { // We assume previous volatile accesses provide equivalent // of release ordering, otherwise we need U.storeFence() here. - endTransition(/*is_mount*/false); + endTransition(/*mount*/false); } /** @@ -549,11 +546,11 @@ final class VirtualThread extends BaseVirtualThread { */ @Hidden private boolean yieldContinuation() { - startTransition(/*is_mount*/false); + startTransition(/*mount*/false); try { return Continuation.yield(VTHREAD_SCOPE); } finally { - endTransition(/*is_mount*/true); + endTransition(/*mount*/true); } } @@ -931,28 +928,19 @@ final class VirtualThread extends BaseVirtualThread { */ private void waitTimeoutExpired(byte seqNo) { assert !Thread.currentThread().isVirtual(); - for (;;) { - boolean unblocked = false; - synchronized (timedWaitLock()) { - if (seqNo != timedWaitSeqNo) { - // this timeout task is for a past timed-wait - return; - } - int s = state(); - if (s == TIMED_WAIT) { - unblocked = compareAndSetState(TIMED_WAIT, UNBLOCKED); - } else if (s != (TIMED_WAIT | SUSPENDED)) { - // notified or interrupted, no longer waiting - return; - } - } - if (unblocked) { - lazySubmitRunContinuation(); + + synchronized (timedWaitLock()) { + if (seqNo != timedWaitSeqNo) { + // this timeout task is for a past timed-wait + return; + } + if (!compareAndSetState(TIMED_WAIT, UNBLOCKED)) { + // already notified (or interrupted) return; } - // need to retry when thread is suspended in time-wait - Thread.yield(); } + + lazySubmitRunContinuation(); } /** @@ -1120,8 +1108,7 @@ final class VirtualThread extends BaseVirtualThread { @Override Thread.State threadState() { - int s = state(); - switch (s & ~SUSPENDED) { + switch (state()) { case NEW: return Thread.State.NEW; case STARTED: @@ -1189,85 +1176,6 @@ final class VirtualThread extends BaseVirtualThread { return (state == TERMINATED); } - @Override - StackTraceElement[] asyncGetStackTrace() { - StackTraceElement[] stackTrace; - do { - stackTrace = (carrierThread != null) - ? super.asyncGetStackTrace() // mounted - : tryGetStackTrace(); // unmounted - if (stackTrace == null) { - Thread.yield(); - } - } while (stackTrace == null); - return stackTrace; - } - - /** - * Returns the stack trace for this virtual thread if it is unmounted. - * Returns null if the thread is mounted or in transition. - */ - private StackTraceElement[] tryGetStackTrace() { - int initialState = state() & ~SUSPENDED; - switch (initialState) { - case NEW, STARTED, TERMINATED -> { - return new StackTraceElement[0]; // unmounted, empty stack - } - case RUNNING, PINNED, TIMED_PINNED -> { - return null; // mounted - } - case PARKED, TIMED_PARKED, BLOCKED, WAIT, TIMED_WAIT -> { - // unmounted, not runnable - } - case UNPARKED, UNBLOCKED, YIELDED -> { - // unmounted, runnable - } - case PARKING, TIMED_PARKING, BLOCKING, YIELDING, WAITING, TIMED_WAITING -> { - return null; // in transition - } - default -> throw new InternalError("" + initialState); - } - - // thread is unmounted, prevent it from continuing - int suspendedState = initialState | SUSPENDED; - if (!compareAndSetState(initialState, suspendedState)) { - return null; - } - - // get stack trace and restore state - StackTraceElement[] stack; - try { - stack = cont.getStackTrace(); - } finally { - assert state == suspendedState; - setState(initialState); - } - boolean resubmit = switch (initialState) { - case UNPARKED, UNBLOCKED, YIELDED -> { - // resubmit as task may have run while suspended - yield true; - } - case PARKED, TIMED_PARKED -> { - // resubmit if unparked while suspended - yield parkPermit && compareAndSetState(initialState, UNPARKED); - } - case BLOCKED -> { - // resubmit if unblocked while suspended - yield blockPermit && compareAndSetState(BLOCKED, UNBLOCKED); - } - case WAIT, TIMED_WAIT -> { - // resubmit if notified or interrupted while waiting (Object.wait) - // waitTimeoutExpired will retry if the timed expired when suspended - yield (notified || interrupted) && compareAndSetState(initialState, UNBLOCKED); - } - default -> throw new InternalError(); - }; - if (resubmit) { - submitRunContinuation(); - } - return stack; - } - @Override public String toString() { StringBuilder sb = new StringBuilder("VirtualThread[#"); @@ -1438,11 +1346,11 @@ final class VirtualThread extends BaseVirtualThread { @IntrinsicCandidate @JvmtiMountTransition - private native void startTransition(boolean is_mount); + private native void startTransition(boolean mount); @IntrinsicCandidate @JvmtiMountTransition - private native void endTransition(boolean is_mount); + private native void endTransition(boolean mount); @IntrinsicCandidate private static native void notifyJvmtiDisableSuspend(boolean enter); diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index b2a224f5917..e578bb2f6ff 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -640,4 +640,9 @@ public interface JavaLangAccess { * Are the string bytes compatible with the given charset? */ boolean bytesCompatible(String string, Charset charset, int srcIndex, int numChars); + + /** + * Finish initialization of the StackTraceElement objects in a stack trace. + */ + void finishInit(StackTraceElement[] stackTrace); } diff --git a/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java b/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java index 357d38008d1..e84027764f1 100644 --- a/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.java +++ b/src/java.base/share/classes/jdk/internal/vm/ThreadSnapshot.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 @@ -26,11 +26,14 @@ package jdk.internal.vm; import java.util.Arrays; import java.util.stream.Stream; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; /** * Represents a snapshot of information about a Thread. */ class ThreadSnapshot { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static final StackTraceElement[] EMPTY_STACK = new StackTraceElement[0]; private static final ThreadLock[] EMPTY_LOCKS = new ThreadLock[0]; @@ -54,16 +57,16 @@ class ThreadSnapshot { /** * Take a snapshot of a Thread to get all information about the thread. - * Return null if a ThreadSnapshot is not created, for example if the - * thread has terminated. - * @throws UnsupportedOperationException if not supported by VM + * @return the snapshot or {@code null} if the thread is not alive */ static ThreadSnapshot of(Thread thread) { - ThreadSnapshot snapshot = create(thread); + ThreadSnapshot snapshot = thread.isAlive() ? create(thread) : null; if (snapshot == null) { - return null; // thread terminated + return null; // thread not alive } - if (snapshot.stackTrace == null) { + if (snapshot.stackTrace != null) { + JLA.finishInit(snapshot.stackTrace); + } else { snapshot.stackTrace = EMPTY_STACK; } if (snapshot.locks != null) { diff --git a/src/java.base/share/native/libjava/Thread.c b/src/java.base/share/native/libjava/Thread.c index 4ac30638bc2..15e9614ae73 100644 --- a/src/java.base/share/native/libjava/Thread.c +++ b/src/java.base/share/native/libjava/Thread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -47,7 +47,7 @@ static JNINativeMethod methods[] = { {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock}, {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, - {"getStackTrace0", "()" OBJ, (void *)&JVM_GetStackTrace}, + {"getStackTrace0", "()[" STE, (void *)&JVM_GetStackTrace}, {"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName}, {"scopedValueCache", "()[" OBJ, (void *)&JVM_ScopedValueCache}, {"setScopedValueCache", "([" OBJ ")V",(void *)&JVM_SetScopedValueCache}, diff --git a/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadNotAlive.java b/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadNotAlive.java new file mode 100644 index 00000000000..6b9905e2fa4 --- /dev/null +++ b/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadNotAlive.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. + */ + +/** + * @test + * @summary Test jdk.internal.vm.ThreadSnapshot.of(Thread) when thread is not alive + * @modules java.base/jdk.internal.vm + * @compile/module=java.base jdk/internal/vm/Helper.java + * @run junit ThreadNotAlive + */ + +import jdk.internal.vm.Helper; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class ThreadNotAlive { + + @Test + void unstartedPlatformThread() { + Thread t = Thread.ofPlatform().unstarted(() -> { }); + assertFalse(Helper.isAlive(t)); + } + + @Test + void terminatedPlatformThread() throws InterruptedException { + Thread t = Thread.ofPlatform().start(() -> { }); + t.join(); + assertFalse(Helper.isAlive(t)); + } + + @Test + void unstartedVirtualhread() { + Thread t = Thread.ofVirtual().unstarted(() -> { }); + assertFalse(Helper.isAlive(t)); + } + + @Test + void terminatedVirtualThread() throws InterruptedException { + Thread t = Thread.ofVirtual().start(() -> { }); + t.join(); + assertFalse(Helper.isAlive(t)); + } + +} diff --git a/test/jdk/jdk/internal/vm/ThreadSnapshot/java.base/jdk/internal/vm/Helper.java b/test/jdk/jdk/internal/vm/ThreadSnapshot/java.base/jdk/internal/vm/Helper.java new file mode 100644 index 00000000000..99be9d2241a --- /dev/null +++ b/test/jdk/jdk/internal/vm/ThreadSnapshot/java.base/jdk/internal/vm/Helper.java @@ -0,0 +1,38 @@ +/* + * 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.internal.vm; + +/** + * Defines methods that use package-private methods on ThreadSnapshot. + */ +public class Helper { + + /** + * Uses ThreadSnapshot.of(Thread) to take a snapshot of the given thread, returning + * {@code true} if the thread is alive. + */ + public static boolean isAlive(Thread thread) { + return ThreadSnapshot.of(thread) != null; + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/ThreadGetStackTraceWhenParked.java b/test/micro/org/openjdk/bench/java/lang/ThreadGetStackTraceWhenParked.java new file mode 100644 index 00000000000..cd75d3a9568 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ThreadGetStackTraceWhenParked.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 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. + */ +package org.openjdk.bench.java.lang; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; +import org.openjdk.jmh.annotations.*; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(value = 3) +public class ThreadGetStackTraceWhenParked { + private volatile boolean done; + private Thread thread; + + @Setup + public void setup() throws InterruptedException { + var started = new CountDownLatch(1); + thread = Thread.ofPlatform().start(() -> { + started.countDown(); + while (!done) { + LockSupport.park(); + } + }); + started.await(); + } + + @TearDown + public void shutdown() throws InterruptedException { + done = true; + LockSupport.unpark(thread); + thread.join(); + } + + @Benchmark + public StackTraceElement[] testGetStackTrace() { + return thread.getStackTrace(); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/ThreadGetStackTraceWhenSpinning.java b/test/micro/org/openjdk/bench/java/lang/ThreadGetStackTraceWhenSpinning.java new file mode 100644 index 00000000000..26c61d19206 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/ThreadGetStackTraceWhenSpinning.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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. + */ +package org.openjdk.bench.java.lang; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(value = 3) +public class ThreadGetStackTraceWhenSpinning { + private volatile boolean done; + private Thread thread; + + @Setup + public void setup() throws InterruptedException { + var started = new CountDownLatch(1); + thread = Thread.ofPlatform().start(() -> { + started.countDown(); + while (!done) { + Thread.onSpinWait(); + } + }); + started.await(); + } + + @TearDown + public void shutdown() throws InterruptedException { + done = true; + thread.join(); + } + + @Benchmark + public StackTraceElement[] testGetStackTrace() { + return thread.getStackTrace(); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenSpinning.java b/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenSpinning.java new file mode 100644 index 00000000000..59836815bfd --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenSpinning.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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. + */ +package org.openjdk.bench.java.lang; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(value = 3) +public class VirtualThreadGetStackTraceWhenSpinning { + private volatile boolean done; + private Thread thread; + + @Setup + public void setup() throws InterruptedException { + var started = new CountDownLatch(1); + thread = Thread.startVirtualThread(() -> { + started.countDown(); + while (!done) { + Thread.onSpinWait(); + } + }); + started.await(); + } + + @TearDown + public void shutdown() throws InterruptedException { + done = true; + thread.join(); + } + + @Benchmark + public StackTraceElement[] testGetStackTrace() { + return thread.getStackTrace(); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenUnmounted.java b/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenUnmounted.java new file mode 100644 index 00000000000..c972b511bd7 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenUnmounted.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 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. + */ +package org.openjdk.bench.java.lang; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; +import org.openjdk.jmh.annotations.*; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(value = 3) +public class VirtualThreadGetStackTraceWhenUnmounted { + private volatile boolean done; + private Thread thread; + + @Setup + public void setup() throws InterruptedException { + var started = new CountDownLatch(1); + thread = Thread.startVirtualThread(() -> { + started.countDown(); + while (!done) { + LockSupport.park(); + } + }); + started.await(); + } + + @TearDown + public void shutdown() throws InterruptedException { + done = true; + LockSupport.unpark(thread); + thread.join(); + } + + @Benchmark + public StackTraceElement[] testGetStackTrace() { + return thread.getStackTrace(); + } +} diff --git a/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenYielding.java b/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenYielding.java new file mode 100644 index 00000000000..0dbefc697c4 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/VirtualThreadGetStackTraceWhenYielding.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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. + */ +package org.openjdk.bench.java.lang; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.*; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 10, time = 1) +@Fork(value = 3) +public class VirtualThreadGetStackTraceWhenYielding { + private volatile boolean done; + private Thread thread; + + @Setup + public void setup() throws InterruptedException { + var started = new CountDownLatch(1); + thread = Thread.startVirtualThread(() -> { + started.countDown(); + while (!done) { + Thread.yield(); + } + }); + started.await(); + } + + @TearDown + public void shutdown() throws InterruptedException { + done = true; + thread.join(); + } + + @Benchmark + public StackTraceElement[] testGetStackTrace() { + return thread.getStackTrace(); + } +} From 1614714bc2d9891f9a393c3e19f0c92a5713a276 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 5 Feb 2026 14:56:49 +0000 Subject: [PATCH 136/215] 8377150: G1: Full GC should not execute barrier code during reference processing Reviewed-by: sjohanss, iwalulya --- src/hotspot/share/gc/g1/g1FullCollector.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 6c8cc7028cc..06db5f612a1 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -276,6 +276,21 @@ void G1FullCollector::before_marking_update_attribute_table(G1HeapRegion* hr) { class G1FullGCRefProcProxyTask : public RefProcProxyTask { G1FullCollector& _collector; + // G1 Full GC specific closure for handling discovered fields. Do NOT need any + // barriers as Full GC discards all this information anyway. + class G1FullGCDiscoveredFieldClosure : public EnqueueDiscoveredFieldClosure { + G1CollectedHeap* _g1h; + + public: + G1FullGCDiscoveredFieldClosure() : _g1h(G1CollectedHeap::heap()) { } + + void enqueue(HeapWord* discovered_field_addr, oop value) override { + assert(_g1h->is_in(discovered_field_addr), PTR_FORMAT " is not in heap ", p2i(discovered_field_addr)); + // Store the value and done. + RawAccess<>::oop_store(discovered_field_addr, value); + } + }; + public: G1FullGCRefProcProxyTask(G1FullCollector &collector, uint max_workers) : RefProcProxyTask("G1FullGCRefProcProxyTask", max_workers), @@ -286,7 +301,7 @@ public: G1IsAliveClosure is_alive(&_collector); uint index = (_tm == RefProcThreadModel::Single) ? 0 : worker_id; G1FullKeepAliveClosure keep_alive(_collector.marker(index)); - BarrierEnqueueDiscoveredFieldClosure enqueue; + G1FullGCDiscoveredFieldClosure enqueue; G1MarkStackClosure* complete_marking = _collector.marker(index)->stack_closure(); _rp_task->rp_work(worker_id, &is_alive, &keep_alive, &enqueue, complete_marking); } From 22e1f68a259f2932afdb861cd4977fbe000f131f Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Thu, 5 Feb 2026 17:40:05 +0000 Subject: [PATCH 137/215] 8375585: Test java/lang/ProcessBuilder/PipelineLeaksFD.java failed Reviewed-by: jpai, syan --- test/jdk/ProblemList.txt | 1 - .../lang/ProcessBuilder/PipelineLeaksFD.java | 109 +++++++++++------- .../java/lang/ProcessBuilder/TEST.properties | 2 +- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 36356c3c544..94015ebc680 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -503,7 +503,6 @@ java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java 8151492 generic- java/lang/invoke/LFCaching/LFGarbageCollectedTest.java 8078602 generic-all java/lang/invoke/lambda/LambdaFileEncodingSerialization.java 8249079 linux-all java/lang/invoke/RicochetTest.java 8251969 generic-all -java/lang/ProcessBuilder/PipelineLeaksFD.java 8375585 generic-all ############################################################################ diff --git a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java index e7eaa57ec95..92cca736503 100644 --- a/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java +++ b/test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java @@ -34,11 +34,15 @@ import java.lang.ProcessHandle; import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.TimeUnit; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.StringJoiner; import java.util.stream.Collectors; /* @@ -80,7 +84,7 @@ public class PipelineLeaksFD { // Check if lsof is available private static boolean checkForLSOF() { try { - lsofForAll(); + lsofForPids(MY_PID); return true; } catch (IOException ioe) { System.err.println("Skipping: " + ioe); @@ -93,7 +97,7 @@ public class PipelineLeaksFD { @MethodSource("builders") void checkForLeaks(List builders) throws IOException { - List lsofLines = lsofForAll(); + List lsofLines = lsofForPids(MY_PID); Set pipesBefore = pipesFromLSOF(lsofLines, MY_PID); if (pipesBefore.size() < 3) { // Dump all lsof output to aid debugging @@ -129,7 +133,7 @@ public class PipelineLeaksFD { processes.forEach(PipelineLeaksFD::waitForQuiet); - lsofLines = lsofForAll(); + lsofLines = lsofForPids(MY_PID); Set pipesAfter = pipesFromLSOF(lsofLines, MY_PID); if (!pipesBefore.equals(pipesAfter)) { Set missing = new HashSet<>(pipesBefore); @@ -157,22 +161,31 @@ public class PipelineLeaksFD { @EnabledIf("lsofAvailable") @ParameterizedTest() @MethodSource("redirectCases") - void checkRedirectErrorStream(boolean redirectError) throws IOException { + void checkRedirectErrorStream(boolean redirectError) { try (Process p = new ProcessBuilder("cat") .redirectErrorStream(redirectError) .start()) { System.err.printf("Parent PID; %d, Child Pid: %d\n", MY_PID, p.pid()); - List lsofLines = lsofForAll(); - final Set pipes = pipesFromLSOF(lsofLines, p.pid()); - printPipes(pipes, "Parent and waiting child pipes"); - int uniquePipes = redirectError ? 8 : 9; - if (uniquePipes != pipes.size()) { - // Dump all lsof output to aid debugging - System.out.println("\nOutput from lsof"); - lsofLines.forEach(System.err::println); - } - assertEquals(uniquePipes, pipes.size(), - "wrong number of pipes for redirect: " + redirectError); + List lsofLines = lsofForPids(MY_PID, p.pid()); + final Set pipes = pipesFromLSOF(lsofLines, MY_PID, p.pid()); + printPipes(pipes, "Parent and child pipes"); + + // For each of the child standard pipes, check the parent and child uses of the pipe + var pIn = PipeRecord.pipeFor(pipes, p.pid(), 0).orElseThrow(); + Set pIns = PipeRecord.allSamePipes(pipes, pIn); + assertEquals(2, pIns.size(), "StdIn pipe count"); + + // Number of pipe references depending on whether stderr is redirected to stdout + long expected = redirectError ? 3 : 2; + + var pOut = PipeRecord.pipeFor(pipes, p.pid(), 1).orElseThrow(); + Set pOuts = PipeRecord.allSamePipes(pipes, pOut); + assertEquals(expected, pOuts.size(), "StdOut pipe count"); + + var pErr = PipeRecord.pipeFor(pipes, p.pid(), 2).orElseThrow(); + Set pErrs = PipeRecord.allSamePipes(pipes, pErr); + assertEquals(expected, pErrs.size(), "StdErr pipe count"); + String expectedTypeName = redirectError ? "java.lang.ProcessBuilder$NullInputStream" : "java.lang.ProcessImpl$ProcessPipeInputStream"; @@ -199,42 +212,45 @@ public class PipelineLeaksFD { } /** - * Collect a Set of file descriptors and identifying information. - * To identify the pipes in use the `lsof` command is invoked and output scrapped for - * fd's, pids, unique identities of the pipes (to match with parent). - * @return A set of PipeRecords, possibly empty - */ - private static Set pipesForPid(long pid) throws IOException { - var lines = lsofForAll(); - return pipesFromLSOF(lines, pid); - } - - /** - * Extract the PipeRecords from the `lsof` output for this process and a designated child process. + * Extract the PipeRecords from the `lsof` output for a list of processes * * @param lines lines of lsof output - * @param pid pid of child process of interest - * @return a Set of PipeRecords for parent and child + * @param pids varargs list of pids of interest + * @return a Set of PipeRecords for those pids */ - private static LinkedHashSet pipesFromLSOF(List lines, long pid) { + private static LinkedHashSet pipesFromLSOF(List lines, long... pids) { + Arrays.sort(pids); return lines.stream() .map(PipelineLeaksFD::pipeFromLSOF) .filter(pr -> pr != null && - (pr.pid() == pid || pr.pid() == MY_PID)) + Arrays.binarySearch(pids, pr.pid()) >= 0) .collect(Collectors.toCollection(LinkedHashSet::new)); } /** * Collect the output of `lsof` for all files. * Files are used for `lsof` input and output to avoid creating pipes. + * @param pids zero or more pids to request data for; none -> all * @return a List of lines output from `lsof`. */ - private static List lsofForAll() throws IOException { + private static List lsofForPids(long... pids) throws IOException { Path tmpDir = Path.of("."); String tmpPrefix = "lsof-"; Path lsofEmptyInput = Files.createTempFile(tmpDir, tmpPrefix, ".empty"); Path lsofOutput = Files.createTempFile(tmpDir, tmpPrefix, ".tmp"); - try (Process p = new ProcessBuilder("lsof") + + List lsofArgs = new ArrayList<>(); + lsofArgs.add("lsof"); + if (pids.length > 0) { + StringJoiner pidsArg = new StringJoiner(","); + for (long p : pids) { + pidsArg.add(Long.toString(p)); + } + lsofArgs.add("-p"); + lsofArgs.add(pidsArg.toString()); + } + + try (Process p = new ProcessBuilder(lsofArgs) .redirectOutput(lsofOutput.toFile()) .redirectInput(lsofEmptyInput.toFile()) // empty input .redirectError(ProcessBuilder.Redirect.DISCARD) // ignored output @@ -261,8 +277,8 @@ public class PipelineLeaksFD { } // Return Pipe from lsof output put, or null (on Mac OS X) - // lsof 55221 rriggs 0 PIPE 0xc76402237956a5cb 16384 ->0xfcb0c07ae447908c - // lsof 55221 rriggs 1 PIPE 0xb486e02f86da463e 16384 ->0xf94eacc85896b4e6 + // lsof 55221 xxxx 0 PIPE 0xc76402237956a5cb 16384 ->0xfcb0c07ae447908c + // lsof 55221 xxxx 1 PIPE 0xb486e02f86da463e 16384 ->0xf94eacc85896b4e6 static PipeRecord pipeFromMacLSOF(String s) { String[] fields = s.split("\\s+"); if ("PIPE".equals(fields[4])) { @@ -275,8 +291,8 @@ public class PipelineLeaksFD { } // Return Pipe from lsof output put, or null (on Linux) - // java 7612 rriggs 14w FIFO 0,12 0t0 117662267 pipe - // java 7612 rriggs 15r FIFO 0,12 0t0 117662268 pipe + // java 7612 xxxx 14w FIFO 0,12 0t0 117662267 pipe + // java 7612 xxxx 15r FIFO 0,12 0t0 117662268 pipe static PipeRecord pipeFromLinuxLSOF(String s) { String[] fields = s.split("\\s+"); if ("FIFO".equals(fields[4])) { @@ -294,6 +310,20 @@ public class PipelineLeaksFD { static PipeRecord lookup(int fd, String myKey, String otherKey, int pid) { return new PipeRecord(pid, fd, KeyedString.getKey(myKey, otherKey)); } + + // Return the PipeRecord matching the fd and pid + static Optional pipeFor(Set pipes, long pid, int fd) { + return pipes.stream() + .filter(p -> p.fd() == fd && p.pid() == pid) + .findFirst(); + } + + // Return all the PipeRecords with the same key (the same OS pipe identification) + static Set allSamePipes(Set pipes, PipeRecord p) { + return pipes.stream() + .filter(p1 -> p1.myKey().key.equals(p.myKey().key)) + .collect(Collectors.toSet()); + } } // A unique name for a string with a count of uses @@ -303,11 +333,9 @@ public class PipelineLeaksFD { private static int nextInt = 1; private final String key; private final String name; - private int count; KeyedString(String key, String name) { this.key = key; this.name = name; - this.count = 0; } KeyedString(String s) { @@ -317,7 +345,6 @@ public class PipelineLeaksFD { static KeyedString getKey(String key, String otherKey) { var k = map.computeIfAbsent(key, KeyedString::new); - k.count++; if (otherKey != null) { map.putIfAbsent(otherKey, k); } @@ -325,7 +352,7 @@ public class PipelineLeaksFD { } public String toString() { - return name + "(" + count + ")"; + return name + ": osInfo: " + key; } } } diff --git a/test/jdk/java/lang/ProcessBuilder/TEST.properties b/test/jdk/java/lang/ProcessBuilder/TEST.properties index c7e8a6850ca..f96e7602fd4 100644 --- a/test/jdk/java/lang/ProcessBuilder/TEST.properties +++ b/test/jdk/java/lang/ProcessBuilder/TEST.properties @@ -1 +1 @@ -maxOutputSize=6000000 +maxOutputSize=12000000 From 4e6cf8f5611b6f1ae1d18b01e95216d9bf43ee5a Mon Sep 17 00:00:00 2001 From: David Beaumont Date: Thu, 5 Feb 2026 17:44:06 +0000 Subject: [PATCH 138/215] 8377244: Update ModuleReaderTest to JUnit prior to making changes Reviewed-by: rriggs --- .../module/ModuleReader/ModuleReaderTest.java | 149 ++++++------------ 1 file changed, 50 insertions(+), 99 deletions(-) diff --git a/test/jdk/java/lang/module/ModuleReader/ModuleReaderTest.java b/test/jdk/java/lang/module/ModuleReader/ModuleReaderTest.java index 61e5914798e..6217a4a8954 100644 --- a/test/jdk/java/lang/module/ModuleReader/ModuleReaderTest.java +++ b/test/jdk/java/lang/module/ModuleReader/ModuleReaderTest.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. * 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,7 +31,7 @@ * @build ModuleReaderTest * jdk.test.lib.compiler.CompilerUtils * jdk.test.lib.util.JarUtils - * @run testng ModuleReaderTest + * @run junit ModuleReaderTest * @summary Basic tests for java.lang.module.ModuleReader */ @@ -48,22 +48,24 @@ import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import java.util.spi.ToolProvider; import java.util.stream.Stream; import jdk.internal.module.ModulePath; import jdk.test.lib.compiler.CompilerUtils; import jdk.test.lib.util.JarUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ModuleReaderTest { private static final String TEST_SRC = System.getProperty("test.src"); @@ -150,8 +152,8 @@ public class ModuleReaderTest { "p\\Main.class" }; - @BeforeTest - public void compileTestModule() throws Exception { + @BeforeAll + public static void compileTestModule() throws Exception { // javac -d mods/$TESTMODULE src/$TESTMODULE/** boolean compiled = CompilerUtils.compile(SRC_DIR.resolve(TEST_MODULE), MODS_DIR.resolve(TEST_MODULE)); @@ -187,51 +189,29 @@ public class ModuleReaderTest { Optional ouri = reader.find(name); ouri.ifPresent(uri -> { if (name.endsWith("/")) - assertTrue(uri.toString().endsWith("/")); + assertTrue(uri.toString().endsWith("/"), + "mismatched directory URI for '" + name + "': " + uri); }); } // test "not found" in java.base module for (String name : NOT_BASE_RESOURCES) { - assertFalse(reader.find(name).isPresent()); - assertFalse(reader.open(name).isPresent()); - assertFalse(reader.read(name).isPresent()); + assertFalse(reader.find(name).isPresent(), "Unexpected resource found: " + name); + assertFalse(reader.open(name).isPresent(), "Unexpected resource opened: " + name); + assertFalse(reader.read(name).isPresent(), "Unexpected resource read: " + name); } // test nulls - try { - reader.find(null); - assertTrue(false); - } catch (NullPointerException expected) { } - - try { - reader.open(null); - assertTrue(false); - } catch (NullPointerException expected) { } - - try { - reader.read(null); - assertTrue(false); - } catch (NullPointerException expected) { } - - try { - reader.release(null); - assertTrue(false); - } catch (NullPointerException expected) { } - + assertThrows(NullPointerException.class, () -> reader.find(null)); + assertThrows(NullPointerException.class, () -> reader.open(null)); + assertThrows(NullPointerException.class, () -> reader.read(null)); + assertThrows(NullPointerException.class, () -> reader.release(null)); } // test closed ModuleReader - try { - reader.open(BASE_RESOURCES[0]); - assertTrue(false); - } catch (IOException expected) { } - - - try { - reader.read(BASE_RESOURCES[0]); - assertTrue(false); - } catch (IOException expected) { } + assertThrows(IOException.class, () -> reader.open(BASE_RESOURCES[0])); + assertThrows(IOException.class, () -> reader.read(BASE_RESOURCES[0])); + assertThrows(IOException.class, reader::list); } /** @@ -268,10 +248,10 @@ public class ModuleReaderTest { String jmod = dir.resolve("m.jmod").toString(); String[] args = { "create", "--class-path", cp, jmod }; ToolProvider jmodTool = ToolProvider.findFirst("jmod") - .orElseThrow(() -> - new RuntimeException("jmod tool not found") - ); - assertEquals(jmodTool.run(System.out, System.out, args), 0); + .orElseThrow(() -> + new RuntimeException("jmod tool not found") + ); + assertEquals(0, jmodTool.run(System.out, System.out, args), "jmod tool failed"); test(dir); } @@ -307,57 +287,30 @@ public class ModuleReaderTest { Optional ouri = reader.find(name); ouri.ifPresent(uri -> { if (name.endsWith("/")) - assertTrue(uri.toString().endsWith("/")); + assertTrue(uri.toString().endsWith("/"), + "mismatched directory URI for '" + name + "': " + uri); }); } // test "not found" in test module for (String name : NOT_TEST_RESOURCES) { System.out.println("resource: " + name); - assertFalse(reader.find(name).isPresent()); - assertFalse(reader.open(name).isPresent()); - assertFalse(reader.read(name).isPresent()); + assertFalse(reader.find(name).isPresent(), "Unexpected resource found: " + name); + assertFalse(reader.open(name).isPresent(), "Unexpected resource open: " + name); + assertFalse(reader.read(name).isPresent(), "Unexpected resource read: " + name); } // test nulls - try { - reader.find(null); - assertTrue(false); - } catch (NullPointerException expected) { } - - try { - reader.open(null); - assertTrue(false); - } catch (NullPointerException expected) { } - - try { - reader.read(null); - assertTrue(false); - } catch (NullPointerException expected) { } - - try { - reader.release(null); - throw new RuntimeException(); - } catch (NullPointerException expected) { } - + assertThrows(NullPointerException.class, () -> reader.find(null)); + assertThrows(NullPointerException.class, () -> reader.open(null)); + assertThrows(NullPointerException.class, () -> reader.read(null)); + assertThrows(NullPointerException.class, () -> reader.release(null)); } // test closed ModuleReader - try { - reader.open(TEST_RESOURCES[0]); - assertTrue(false); - } catch (IOException expected) { } - - - try { - reader.read(TEST_RESOURCES[0]); - assertTrue(false); - } catch (IOException expected) { } - - try { - reader.list(); - assertTrue(false); - } catch (IOException expected) { } + assertThrows(IOException.class, () -> reader.open(BASE_RESOURCES[0])); + assertThrows(IOException.class, () -> reader.read(BASE_RESOURCES[0])); + assertThrows(IOException.class, reader::list); } /** @@ -367,7 +320,7 @@ public class ModuleReaderTest { throws IOException { Optional ouri = reader.find(name); - assertTrue(ouri.isPresent()); + assertTrue(ouri.isPresent(), "missing URI for: " + name); URL url = ouri.get().toURL(); if (!url.getProtocol().equalsIgnoreCase("jmod")) { @@ -375,7 +328,7 @@ public class ModuleReaderTest { uc.setUseCaches(false); try (InputStream in = uc.getInputStream()) { byte[] bytes = in.readAllBytes(); - assertTrue(Arrays.equals(bytes, expectedBytes)); + assertArrayEquals(expectedBytes, bytes, "resource bytes differ for: " + name); } } } @@ -387,12 +340,10 @@ public class ModuleReaderTest { throws IOException { Optional oin = reader.open(name); - assertTrue(oin.isPresent()); - - InputStream in = oin.get(); - try (in) { + assertTrue(oin.isPresent(), "missing input stream for: " + name); + try (InputStream in = oin.get()) { byte[] bytes = in.readAllBytes(); - assertTrue(Arrays.equals(bytes, expectedBytes)); + assertArrayEquals(expectedBytes, bytes, "resource bytes differ for: " + name); } } @@ -408,10 +359,10 @@ public class ModuleReaderTest { ByteBuffer bb = obb.get(); try { int rem = bb.remaining(); - assertTrue(rem == expectedBytes.length); + assertEquals(expectedBytes.length, rem, "resource lengths differ: " + name); byte[] bytes = new byte[rem]; bb.get(bytes); - assertTrue(Arrays.equals(bytes, expectedBytes)); + assertArrayEquals(expectedBytes, bytes, "resource bytes differ: " + name); } finally { reader.release(bb); } @@ -426,14 +377,14 @@ public class ModuleReaderTest { list = stream.toList(); } Set names = new HashSet<>(list); - assertTrue(names.size() == list.size()); // no duplicates + assertEquals(names.size(), list.size(), "resource list contains duplicates: " + list); - assertTrue(names.contains("module-info.class")); - assertTrue(names.contains(name)); + assertTrue(names.contains("module-info.class"), "resource list did not contain 'module-info.class': " + list); + assertTrue(names.contains(name), "resource list did not contain '" + name + "'" + list); // all resources should be locatable via find for (String e : names) { - assertTrue(reader.find(e).isPresent()); + assertTrue(reader.find(e).isPresent(), "resource not found: " + name); } } From bd9c94d19755232070e88af33147f4a3f21f02f4 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 5 Feb 2026 18:46:15 +0000 Subject: [PATCH 139/215] 8377199: Remove AppContext from AWTKeyStroke Reviewed-by: tr, azvegint --- .../share/classes/java/awt/AWTKeyStroke.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/AWTKeyStroke.java b/src/java.desktop/share/classes/java/awt/AWTKeyStroke.java index fee1c9a4e19..395c000617b 100644 --- a/src/java.desktop/share/classes/java/awt/AWTKeyStroke.java +++ b/src/java.desktop/share/classes/java/awt/AWTKeyStroke.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 @@ -36,7 +36,6 @@ import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; -import sun.awt.AppContext; import sun.swing.SwingAccessor; /** @@ -80,11 +79,6 @@ public class AWTKeyStroke implements Serializable { */ private static VKCollection vks; - //A key for the collection of AWTKeyStrokes within AppContext. - private static Object APP_CONTEXT_CACHE_KEY = new Object(); - //A key within the cache - private static AWTKeyStroke APP_CONTEXT_KEYSTROKE_KEY = new AWTKeyStroke(); - /** * @serial The character value for a keyboard key. */ @@ -181,21 +175,19 @@ public class AWTKeyStroke implements Serializable { protected static void registerSubclass(Class subclass) { } + private static Map cache; + private static AWTKeyStroke cacheKey; + private static synchronized AWTKeyStroke getCachedStroke (char keyChar, int keyCode, int modifiers, boolean onKeyRelease) { - @SuppressWarnings("unchecked") - Map cache = (Map)AppContext.getAppContext().get(APP_CONTEXT_CACHE_KEY); - AWTKeyStroke cacheKey = (AWTKeyStroke)AppContext.getAppContext().get(APP_CONTEXT_KEYSTROKE_KEY); if (cache == null) { cache = new HashMap<>(); - AppContext.getAppContext().put(APP_CONTEXT_CACHE_KEY, cache); } if (cacheKey == null) { cacheKey = SwingAccessor.getKeyStrokeAccessor().create(); - AppContext.getAppContext().put(APP_CONTEXT_KEYSTROKE_KEY, cacheKey); } cacheKey.keyChar = keyChar; @@ -203,11 +195,16 @@ public class AWTKeyStroke implements Serializable { cacheKey.modifiers = mapNewModifiers(mapOldModifiers(modifiers)); cacheKey.onKeyRelease = onKeyRelease; + /* If there's no hit on the cache, then store this instance in the cache + * and null out the var so it isn't over-written. + * But if there's a hit, store it in cacheKey so it can be re-used + * next time this method is called. + */ AWTKeyStroke stroke = cache.get(cacheKey); if (stroke == null) { stroke = cacheKey; cache.put(stroke, stroke); - AppContext.getAppContext().remove(APP_CONTEXT_KEYSTROKE_KEY); + cacheKey = null; } return stroke; } From 99be94e38f69f9c64d9142e44acc22f5689b26f1 Mon Sep 17 00:00:00 2001 From: Justin Lu Date: Thu, 5 Feb 2026 18:56:36 +0000 Subject: [PATCH 140/215] 8377116: Refactor java/util/zip TestNG tests to JUnit Reviewed-by: lancea --- .../util/zip/CloseInflaterDeflaterTest.java | 65 +++++----- .../util/zip/DeflaterDictionaryTests.java | 59 +++++----- .../zip/GZIP/GZIPOutputStreamHeaderTest.java | 16 +-- .../zip/ZipFile/GetInputStreamNPETest.java | 111 ++++++++++-------- .../InvalidBytesInEntryNameOrComment.java | 4 +- .../zip/ZipFile/InvalidCommentLengthTest.java | 41 ++++--- .../zip/ZipFile/MissingZIP64EntriesTest.java | 10 +- .../zip/ZipFile/TestZipFileEncodings.java | 102 ++++++++-------- .../util/zip/ZipFile/ZipEntryTimeBounds.java | 42 +++---- .../ZipFile/ZipFileDuplicateEntryTest.java | 74 ++++++------ .../ZipFile/ZipFileInputStreamSkipTest.java | 81 +++++++------ .../zip/ZipOutputStream/EmptyComment.java | 21 ++-- 12 files changed, 330 insertions(+), 296 deletions(-) diff --git a/test/jdk/java/util/zip/CloseInflaterDeflaterTest.java b/test/jdk/java/util/zip/CloseInflaterDeflaterTest.java index af314581a9e..992d5b98fb9 100644 --- a/test/jdk/java/util/zip/CloseInflaterDeflaterTest.java +++ b/test/jdk/java/util/zip/CloseInflaterDeflaterTest.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 @@ -25,23 +25,29 @@ * @test * @bug 8193682 8278794 8284771 * @summary Test Infinite loop while writing on closed Deflater and Inflater. - * @run testng CloseInflaterDeflaterTest + * @run junit CloseInflaterDeflaterTest */ -import java.io.*; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Random; import java.util.jar.JarOutputStream; +import java.util.stream.Stream; import java.util.zip.DeflaterInputStream; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPOutputStream; import java.util.zip.InflaterOutputStream; -import java.util.zip.ZipOutputStream; import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import static org.testng.Assert.assertThrows; - +import static org.junit.jupiter.api.Assertions.assertThrows; public class CloseInflaterDeflaterTest { @@ -75,36 +81,28 @@ public class CloseInflaterDeflaterTest { private static Random rand = new Random(); /** - * DataProvider to specify whether to use close() or finish() of OutputStream + * MethodSource to specify whether to use close() or finish() of OutputStream * - * @return Entry object indicating which method to use for closing OutputStream + * @return Stream indicating which method to use for closing OutputStream */ - @DataProvider - public Object[][] testOutputStreams() { - return new Object[][] { - { true }, - { false }, - }; + public static Stream testOutputStreams() { + return Stream.of(true, false); } /** - * DataProvider to specify on which outputstream closeEntry() has to be called + * MethodSource to specify on which outputstream closeEntry() has to be called * - * @return Entry object returning either JarOutputStream or ZipOutputStream + * @return Stream consisting of either a JarOutputStream or ZipOutputStream */ - @DataProvider - public Object[][] testZipAndJar() throws IOException{ - return new Object[][] { - { new JarOutputStream(outStream)}, - { new ZipOutputStream(outStream)}, - }; + public static Stream testZipAndJar() throws IOException{ + return Stream.of(new JarOutputStream(outStream), new ZipOutputStream(outStream)); } /** * Add inputBytes array with random bytes to write into OutputStream */ - @BeforeTest - public void before_test() + @BeforeAll + public static void before_test() { rand.nextBytes(inputBytes); } @@ -115,7 +113,8 @@ public class CloseInflaterDeflaterTest { * @param useCloseMethod indicates whether to use Close() or finish() method * @throws IOException if an error occurs */ - @Test(dataProvider = "testOutputStreams") + @ParameterizedTest + @MethodSource("testOutputStreams") public void testGZip(boolean useCloseMethod) throws IOException { GZIPOutputStream gzip = new GZIPOutputStream(outStream); gzip.write(inputBytes, 0, INPUT_LENGTH); @@ -137,7 +136,8 @@ public class CloseInflaterDeflaterTest { * @param useCloseMethod indicates whether to use Close() or finish() method * @throws IOException if an error occurs */ - @Test(dataProvider = "testOutputStreams") + @ParameterizedTest + @MethodSource("testOutputStreams") public void testDeflaterOutputStream(boolean useCloseMethod) throws IOException { DeflaterOutputStream def = new DeflaterOutputStream(outStream); assertThrows(IOException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH)); @@ -175,7 +175,9 @@ public class CloseInflaterDeflaterTest { * @param useCloseMethod indicates whether to use Close() or finish() method * @throws IOException if an error occurs */ - @Test(dataProvider = "testOutputStreams",enabled=false) + @Disabled + @ParameterizedTest + @MethodSource("testOutputStreams") public void testInflaterOutputStream(boolean useCloseMethod) throws IOException { InflaterOutputStream inf = new InflaterOutputStream(outStream); assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH)); @@ -197,7 +199,8 @@ public class CloseInflaterDeflaterTest { * @param zip will be the instance of either JarOutputStream or ZipOutputStream * @throws IOException if an error occurs */ - @Test(dataProvider = "testZipAndJar") + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("testZipAndJar") public void testZipCloseEntry(ZipOutputStream zip) throws IOException { assertThrows(IOException.class , () -> zip.putNextEntry(new ZipEntry(""))); zip.write(inputBytes, 0, INPUT_LENGTH); diff --git a/test/jdk/java/util/zip/DeflaterDictionaryTests.java b/test/jdk/java/util/zip/DeflaterDictionaryTests.java index 570de5408ee..56d5ef24200 100644 --- a/test/jdk/java/util/zip/DeflaterDictionaryTests.java +++ b/test/jdk/java/util/zip/DeflaterDictionaryTests.java @@ -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,23 +21,26 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.stream.IntStream; import java.util.zip.Deflater; import java.util.zip.Inflater; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.testng.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @test * @bug 8252739 * @summary Verify Deflater.setDictionary(dictionary, offset, length) uses the offset - * @run testng/othervm DeflaterDictionaryTests + * @run junit/othervm DeflaterDictionaryTests */ public class DeflaterDictionaryTests { // Output buffer size @@ -50,31 +53,21 @@ public class DeflaterDictionaryTests { private static final int DICTIONARY_LENGTH = 3; /** - * DataProvider with offsets which should be valid for setDictionary + * MethodSource with offsets which should be valid for setDictionary * - * @return valid offset values + * @return Stream of valid offset values */ - @DataProvider(name = "validDictionaryOffsets") - protected Object[][] validDictionaryOffsets() { - return new Object[][]{ - {0}, - {DICTIONARY_OFFSET}, - {DICTIONARY_LENGTH} - }; + protected static IntStream validDictionaryOffsets() { + return IntStream.of(0, DICTIONARY_OFFSET, DICTIONARY_LENGTH); } /** - * DataProvider with invalid offsets for setDictionary + * MethodSource with invalid offsets for setDictionary * - * @return invalid offset values + * @return Stream of invalid offset values */ - @DataProvider(name = "invalidDictionaryOffsets") - protected Object[][] invalidDictionaryOffsets() { - return new Object[][]{ - {-1}, - {DICTIONARY_LENGTH + 2}, - {DICTIONARY.length()} - }; + protected static IntStream invalidDictionaryOffsets() { + return IntStream.of(-1, DICTIONARY_LENGTH + 2, DICTIONARY.length()); } /** @@ -83,7 +76,8 @@ public class DeflaterDictionaryTests { * @param dictionary_offset offset value to be used * @throws Exception if an error occurs */ - @Test(dataProvider = "validDictionaryOffsets") + @ParameterizedTest + @MethodSource("validDictionaryOffsets") public void testByteArray(int dictionary_offset) throws Exception { byte[] input = SRC_DATA.getBytes(UTF_8); byte[] output = new byte[RESULT_SIZE]; @@ -115,8 +109,8 @@ public class DeflaterDictionaryTests { System.out.printf("Inflater::getAdler:%d, length: %d%n", inflater.getAdler(), resultLength); - Assert.assertEquals(SRC_DATA.length(), resultLength); - Assert.assertEquals(input, Arrays.copyOf(result, resultLength)); + assertEquals(SRC_DATA.length(), resultLength); + assertArrayEquals(input, Arrays.copyOf(result, resultLength)); } finally { // Release Resources deflater.end(); @@ -163,8 +157,8 @@ public class DeflaterDictionaryTests { System.out.printf("Inflater::getAdler:%d, length: %d%n", inflater.getAdler(), resultLength); - Assert.assertEquals(SRC_DATA.length(), resultLength); - Assert.assertEquals(input, Arrays.copyOf(result, resultLength)); + assertEquals(resultLength, SRC_DATA.length()); + assertArrayEquals(Arrays.copyOf(result, resultLength), input); } finally { // Release Resources deflater.end(); @@ -217,8 +211,8 @@ public class DeflaterDictionaryTests { System.out.printf("Inflater::getAdler:%d, length: %d%n", inflater.getAdler(), resultLength); - Assert.assertEquals(SRC_DATA.length(), resultLength); - Assert.assertEquals(input, Arrays.copyOf(result, resultLength)); + assertEquals(resultLength, SRC_DATA.length()); + assertArrayEquals(Arrays.copyOf(result, resultLength), input); } finally { // Release Resources deflater.end(); @@ -232,7 +226,8 @@ public class DeflaterDictionaryTests { * * @param dictionary_offset offset value to be used */ - @Test(dataProvider = "invalidDictionaryOffsets") + @ParameterizedTest + @MethodSource("invalidDictionaryOffsets") public void testInvalidOffsets(int dictionary_offset) { byte[] dictionary = DICTIONARY.getBytes(UTF_8); diff --git a/test/jdk/java/util/zip/GZIP/GZIPOutputStreamHeaderTest.java b/test/jdk/java/util/zip/GZIP/GZIPOutputStreamHeaderTest.java index 93c2e91fea8..5fdad3d3b07 100644 --- a/test/jdk/java/util/zip/GZIP/GZIPOutputStreamHeaderTest.java +++ b/test/jdk/java/util/zip/GZIP/GZIPOutputStreamHeaderTest.java @@ -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 @@ -21,8 +21,7 @@ * questions. */ -import org.testng.Assert; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -30,12 +29,15 @@ import java.nio.charset.StandardCharsets; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + /** * @test * @bug 8244706 * @summary Verify that the OS header flag in the stream written out by java.util.zip.GZIPOutputStream * has the correct expected value - * @run testng GZIPOutputStreamHeaderTest + * @run junit GZIPOutputStreamHeaderTest */ public class GZIPOutputStreamHeaderTest { @@ -54,8 +56,8 @@ public class GZIPOutputStreamHeaderTest { gzipOutputStream.write(data.getBytes(StandardCharsets.UTF_8)); } final byte[] compressed = baos.toByteArray(); - Assert.assertNotNull(compressed, "Compressed data is null"); - Assert.assertEquals(toUnsignedByte(compressed[OS_HEADER_INDEX]), HEADER_VALUE_OS_UNKNOWN, + assertNotNull(compressed, "Compressed data is null"); + assertEquals(HEADER_VALUE_OS_UNKNOWN, toUnsignedByte(compressed[OS_HEADER_INDEX]), "Unexpected value for OS header"); // finally verify that the compressed data is readable back to the original final String uncompressed; @@ -65,7 +67,7 @@ public class GZIPOutputStreamHeaderTest { gzipInputStream.transferTo(os); uncompressed = new String(os.toByteArray(), StandardCharsets.UTF_8); } - Assert.assertEquals(uncompressed, data, "Unexpected data read from GZIPInputStream"); + assertEquals(data, uncompressed, "Unexpected data read from GZIPInputStream"); } private static int toUnsignedByte(final byte b) { diff --git a/test/jdk/java/util/zip/ZipFile/GetInputStreamNPETest.java b/test/jdk/java/util/zip/ZipFile/GetInputStreamNPETest.java index ec7f0fc7d45..a4111ecef35 100644 --- a/test/jdk/java/util/zip/ZipFile/GetInputStreamNPETest.java +++ b/test/jdk/java/util/zip/ZipFile/GetInputStreamNPETest.java @@ -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,10 +22,12 @@ * */ -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.io.IOException; import java.nio.file.Files; @@ -34,16 +36,20 @@ import java.util.Arrays; import java.util.Formatter; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @test * @bug 8280409 * @summary Validate that Zip/JarFile::getInputStream will throw a NullPointerException - * @run testng/othervm GetInputStreamNPETest + * @run junit/othervm GetInputStreamNPETest */ public class GetInputStreamNPETest { // Name used to create a JAR with an invalid entry name @@ -750,33 +756,31 @@ public class GetInputStreamNPETest { }; /** - * DataProvider used to specify valid jars and whether to verify them + * MethodSource used to specify valid jars and whether to verify them * - * @return Entry object indicating the jar file and whether it will be verified + * @return Stream of Arguments which indicate the jar file and whether it will be verified */ - @DataProvider - public Object[][] validJars() { - return new Object[][]{ - {SIGNED_VALID_ENTRY_NAME_JAR, true}, - {SIGNED_VALID_ENTRY_NAME_JAR, false}, - {VALID_ENTRY_NAME_JAR, true}, - {VALID_ENTRY_NAME_JAR, false}, - }; + public static Stream validJars() { + return Stream.of( + Arguments.of(SIGNED_VALID_ENTRY_NAME_JAR, true), + Arguments.of(SIGNED_VALID_ENTRY_NAME_JAR, false), + Arguments.of(VALID_ENTRY_NAME_JAR, true), + Arguments.of(VALID_ENTRY_NAME_JAR, false) + ); } /** - * DataProvider used to specify invalid jars and whether to verify them + * MethodSource used to specify invalid jars and whether to verify them * - * @return Entry object indicating the jar file and whether it will be verified + * @return Stream of Arguments which indicate the jar file and whether it will be verified */ - @DataProvider - public Object[][] inValidJars() { - return new Object[][]{ - {SIGNED_INVALID_ENTRY_NAME_JAR, true}, - {SIGNED_INVALID_ENTRY_NAME_JAR, false}, - {INVALID_ENTRY_NAME_JAR, true}, - {INVALID_ENTRY_NAME_JAR, false}, - }; + public static Stream inValidJars() { + return Stream.of( + Arguments.of(SIGNED_INVALID_ENTRY_NAME_JAR, true), + Arguments.of(SIGNED_INVALID_ENTRY_NAME_JAR, false), + Arguments.of(INVALID_ENTRY_NAME_JAR, true), + Arguments.of(INVALID_ENTRY_NAME_JAR, false) + ); } /** @@ -796,7 +800,7 @@ public class GetInputStreamNPETest { * @throws IOException If an error occurs * */ - @BeforeTest + @BeforeAll public static void setup() throws IOException { // Create valid jar @@ -832,7 +836,7 @@ public class GetInputStreamNPETest { * * @throws IOException If an error occurs */ - @AfterTest + @AfterAll public static void cleanup() throws IOException { Files.deleteIfExists(VALID_ENTRY_NAME_JAR); Files.deleteIfExists(SIGNED_VALID_ENTRY_NAME_JAR); @@ -849,8 +853,9 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified * @throws Exception if an error occurs */ - @Test(dataProvider = "validJars") - public static void validJarFileZipEntryTest(Path jar, boolean verify) throws Exception { + @ParameterizedTest + @MethodSource("validJars") + public void validJarFileZipEntryTest(Path jar, boolean verify) throws Exception { try (JarFile jf = new JarFile(jar.toFile(), verify)) { ZipEntry ze = jf.getEntry(CEN_FILENAME_TO_MODIFY); var is = jf.getInputStream(ze); @@ -868,8 +873,9 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified * @throws Exception if an error occurs */ - @Test(dataProvider = "validJars") - public static void validJarFileJarEntryTest(Path jar, boolean verify) throws Exception { + @ParameterizedTest + @MethodSource("validJars") + public void validJarFileJarEntryTest(Path jar, boolean verify) throws Exception { try (JarFile jf = new JarFile(jar.toFile(), verify)) { ZipEntry ze = jf.getEntry(CEN_FILENAME_TO_MODIFY); var is = jf.getInputStream(ze); @@ -886,8 +892,9 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified(not used) * @throws Exception if an error occurs */ - @Test(dataProvider = "validJars") - public static void validZipFileZipEntryTest(Path jar, boolean verify) throws Exception { + @ParameterizedTest + @MethodSource("validJars") + public void validZipFileZipEntryTest(Path jar, boolean verify) throws Exception { try (ZipFile jf = new ZipFile(jar.toFile())) { ZipEntry ze = jf.getEntry(CEN_FILENAME_TO_MODIFY); var is = jf.getInputStream(ze); @@ -904,12 +911,13 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified(not used) * @throws Exception if an error occurs */ - @Test(dataProvider = "inValidJars") - public static void invalidJarFileZipEntry(Path jar, boolean verify) throws Exception { + @ParameterizedTest + @MethodSource("inValidJars") + public void invalidJarFileZipEntry(Path jar, boolean verify) throws Exception { try (JarFile jf = new JarFile(jar.toFile(), verify)) { // The entry will not be found resulting in the ZipEntry being null ZipEntry ze = jf.getEntry(CEN_FILENAME_TO_MODIFY); - var ex= expectThrows(NullPointerException.class, + var ex= assertThrows(NullPointerException.class, () -> jf.getInputStream(ze) ); // Validate that we receive the expected message from Objects.requireNonNull assertTrue( ex != null && ex.getMessage().equals("ze")); @@ -923,12 +931,13 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified(not used) * @throws IOException if an error occurs */ - @Test(dataProvider = "inValidJars") - public static void invalidZipFileZipEntry(Path jar, boolean verify) throws Exception { + @ParameterizedTest + @MethodSource("inValidJars") + public void invalidZipFileZipEntry(Path jar, boolean verify) throws Exception { try (ZipFile jf = new ZipFile(jar.toFile())) { // The entry will not be found resulting in the ZipEntry being null ZipEntry ze = jf.getEntry(CEN_FILENAME_TO_MODIFY); - var ex= expectThrows(NullPointerException.class, + var ex= assertThrows(NullPointerException.class, () -> jf.getInputStream(ze) ); // Validate that we receive the expected message from Objects.requireNonNull assertTrue( ex != null && ex.getMessage().equals("entry")); @@ -943,8 +952,9 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified * @throws Exception if an error occurs */ - @Test(dataProvider = "validJars") - public static void JarFileZipEntryDoesNotExistGetInputStreamTest( + @ParameterizedTest + @MethodSource("validJars") + public void JarFileZipEntryDoesNotExistGetInputStreamTest( Path jar, boolean verify) throws Exception { try (JarFile jf = new JarFile(jar.toFile(), verify)) { @@ -962,8 +972,9 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified(not used) * @throws Exception if an error occurs */ - @Test(dataProvider = "validJars") - public static void ZipFileZipEntryDoesNotExistGetInputStreamTest( + @ParameterizedTest + @MethodSource("validJars") + public void ZipFileZipEntryDoesNotExistGetInputStreamTest( Path jar, boolean verify) throws Exception { try (ZipFile jf = new ZipFile(jar.toFile())) { var ze = new ZipEntry(ZIP_ENTRY_THAT_DOES_NOT_EXIST); @@ -980,8 +991,9 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified * @throws Exception if an error occurs */ - @Test(dataProvider = "validJars") - public static void JarFileJarEntryEntryDoesNotExistGetInputStreamTest ( + @ParameterizedTest + @MethodSource("validJars") + public void JarFileJarEntryEntryDoesNotExistGetInputStreamTest ( Path jar, boolean verify) throws Exception { try (JarFile jf = new JarFile(jar.toFile(), verify)) { var je = new JarEntry(ZIP_ENTRY_THAT_DOES_NOT_EXIST); @@ -999,8 +1011,9 @@ public class GetInputStreamNPETest { * @param verify indicates whether the jar should be verified * @throws Exception if an error occurs */ - @Test(dataProvider = "validJars") - public static void JarFileZipEntryGetNameNullTest(Path jar, boolean verify) throws Exception { + @ParameterizedTest + @MethodSource("validJars") + public void JarFileZipEntryGetNameNullTest(Path jar, boolean verify) throws Exception { // Signed Jar is used for the next checks try (JarFile jf = new JarFile(jar.toFile(), verify)) { diff --git a/test/jdk/java/util/zip/ZipFile/InvalidBytesInEntryNameOrComment.java b/test/jdk/java/util/zip/ZipFile/InvalidBytesInEntryNameOrComment.java index c43157248f4..71086abb325 100644 --- a/test/jdk/java/util/zip/ZipFile/InvalidBytesInEntryNameOrComment.java +++ b/test/jdk/java/util/zip/ZipFile/InvalidBytesInEntryNameOrComment.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 @@ -169,7 +169,7 @@ public class InvalidBytesInEntryNameOrComment { } /** - * The DataProvider of CEN offsets to modify with an invalid UTF-8 byte + * The MethodSource of CEN offsets to modify with an invalid UTF-8 byte * sequence * * @return Arguments used in each test run diff --git a/test/jdk/java/util/zip/ZipFile/InvalidCommentLengthTest.java b/test/jdk/java/util/zip/ZipFile/InvalidCommentLengthTest.java index 1910107f0d3..7ba33bee740 100644 --- a/test/jdk/java/util/zip/ZipFile/InvalidCommentLengthTest.java +++ b/test/jdk/java/util/zip/ZipFile/InvalidCommentLengthTest.java @@ -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,9 +22,10 @@ * */ -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; @@ -35,14 +36,16 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @test * @bug 8280404 * @summary Validate that Zip/JarFile will throw a ZipException when the CEN * comment length field contains an incorrect value - * @run testng/othervm InvalidCommentLengthTest + * @run junit/othervm InvalidCommentLengthTest */ public class InvalidCommentLengthTest { @@ -264,8 +267,8 @@ public class InvalidCommentLengthTest { * * @throws IOException If an error occurs */ - @BeforeTest - public void setup() throws IOException { + @BeforeAll + public static void setup() throws IOException { Files.deleteIfExists(VALID_CEN_COMMENT_LENGTH_JAR); Files.deleteIfExists(INVALID_CEN_COMMENT_LENGTH_JAR); // Create the valid jar @@ -285,7 +288,7 @@ public class InvalidCommentLengthTest { * * @throws IOException If an error occurs */ - @AfterTest + @AfterAll public static void cleanup() throws IOException { Files.deleteIfExists(VALID_CEN_COMMENT_LENGTH_JAR); Files.deleteIfExists(INVALID_CEN_COMMENT_LENGTH_JAR); @@ -297,11 +300,11 @@ public class InvalidCommentLengthTest { * @throws IOException If an error occurs */ @Test - public static void ZipFileValidCommentLengthTest() throws IOException { + public void ZipFileValidCommentLengthTest() throws IOException { try (ZipFile jf = new ZipFile(VALID_CEN_COMMENT_LENGTH_JAR.toFile())) { ZipEntry ze = jf.getEntry(META_INF_MANIFEST_MF); assertNotNull(ze); - assertEquals(ze.getName(), META_INF_MANIFEST_MF); + assertEquals(META_INF_MANIFEST_MF, ze.getName()); } } @@ -311,11 +314,11 @@ public class InvalidCommentLengthTest { * @throws IOException If an error occurs */ @Test - public static void JarFileValidCommentLengthTest() throws IOException { + public void JarFileValidCommentLengthTest() throws IOException { try (JarFile jf = new JarFile(VALID_CEN_COMMENT_LENGTH_JAR.toFile())) { ZipEntry ze = jf.getEntry(META_INF_MANIFEST_MF); assertNotNull(ze); - assertEquals(ze.getName(), META_INF_MANIFEST_MF); + assertEquals(META_INF_MANIFEST_MF, ze.getName()); } } @@ -325,10 +328,10 @@ public class InvalidCommentLengthTest { * the Jar file is opened by {@code ZipFile} */ @Test - public static void ZipFileInValidCommentLengthTest() { - var ex= expectThrows(ZipException.class, + public void ZipFileInValidCommentLengthTest() { + var ex= assertThrows(ZipException.class, () -> new ZipFile(INVALID_CEN_COMMENT_LENGTH_JAR.toFile())); - assertEquals(ex.getMessage(), INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT); + assertEquals(INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT, ex.getMessage()); } /** @@ -337,9 +340,9 @@ public class InvalidCommentLengthTest { * the Jar file is opened by {@code JarFile} */ @Test - public static void JarFileInValidCommentLengthTest() { - var ex= expectThrows(ZipException.class, + public void JarFileInValidCommentLengthTest() { + var ex= assertThrows(ZipException.class, () -> new JarFile(INVALID_CEN_COMMENT_LENGTH_JAR.toFile())); - assertEquals(ex.getMessage(), INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT); + assertEquals(INVALID_CEN_HEADER_BAD_ENTRY_NAME_OR_COMMENT, ex.getMessage()); } } diff --git a/test/jdk/java/util/zip/ZipFile/MissingZIP64EntriesTest.java b/test/jdk/java/util/zip/ZipFile/MissingZIP64EntriesTest.java index 51a6d0b58ee..4920fa724ce 100644 --- a/test/jdk/java/util/zip/ZipFile/MissingZIP64EntriesTest.java +++ b/test/jdk/java/util/zip/ZipFile/MissingZIP64EntriesTest.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -480,7 +480,7 @@ public class MissingZIP64EntriesTest { private ByteBuffer buffer; /** - * The DataProvider returning a byte array representing the Zip file, + * The MethodSource returning a byte array representing the Zip file, * CEN offsets to set to 0xFFFFFFFF and the expected * ZipException error message when there are missing Zip64 Extra header fields * @return Arguments used in each test run @@ -501,7 +501,7 @@ public class MissingZIP64EntriesTest { } /** - * The DataProvider of CEN offsets to set to 0xFFFFFFFF or 0xFFFF when the Extra Length + * The MethodSource of CEN offsets to set to 0xFFFFFFFF or 0xFFFF when the Extra Length * size is 0 for the Zip file created using ZIP_WITH_NO_EXTRA_LEN_BYTEARRAY * @return Arguments used in each test run */ @@ -519,7 +519,7 @@ public class MissingZIP64EntriesTest { } /** - * The DataProvider of CEN offsets to set to 0xFFFFFFFF when the ZIP64 extra header + * The MethodSource of CEN offsets to set to 0xFFFFFFFF when the ZIP64 extra header * Length size is 0 for the Zip file created using * ZIP_WITH_ZEROLEN_ZIP64_EXTRAHDR_BYTEARRAY * @return Arguments used in each test run @@ -536,7 +536,7 @@ public class MissingZIP64EntriesTest { } /** - * The DataProvider which will return a byte array representing a + * The MethodSource which will return a byte array representing a * valid Zip file and the expected content for the Zip file entry 'Hello.txt'. * @return Arguments used in each test run */ diff --git a/test/jdk/java/util/zip/ZipFile/TestZipFileEncodings.java b/test/jdk/java/util/zip/ZipFile/TestZipFileEncodings.java index 4e271912345..d8692ee3261 100644 --- a/test/jdk/java/util/zip/ZipFile/TestZipFileEncodings.java +++ b/test/jdk/java/util/zip/ZipFile/TestZipFileEncodings.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,11 +26,13 @@ * @bug 8243254 * @summary Tests a simple set of operations on Zip files in various encodings * focusing on ensuring metadata is properly encoded and read. - * @run testng/timeout=480 TestZipFileEncodings + * @run junit/timeout=480 TestZipFileEncodings */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.io.BufferedOutputStream; import java.io.File; @@ -38,7 +40,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -50,8 +51,6 @@ import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; @@ -60,7 +59,10 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class TestZipFileEncodings { @@ -76,50 +78,49 @@ public class TestZipFileEncodings { return ThreadLocalRandom.current(); } - @DataProvider(name = "non-unicode-charsets") - public Object[][] nonUnicodeCharsets() { - return new Object[][] { - { "ISO-8859-1" }, - { "IBM01149" }, - { "IBM037" }, - { "IBM-Thai" } - }; + public static Stream nonUnicodeCharsets() { + return Stream.of( + "ISO-8859-1", + "IBM01149", + "IBM037", + "IBM-Thai" + ); } - @DataProvider(name = "unicode-charsets") - public Object[][] unicodeCharsets() { - return new Object[][] { - { "UTF-8" }, - { "UTF-16" }, - { "UTF-16LE" }, - { "UTF-16BE" }, - { "UTF-32" } - }; + public static Stream unicodeCharsets() { + return Stream.of( + "UTF-8", + "UTF-16", + "UTF-16LE", + "UTF-16BE", + "UTF-32" + ); } - @DataProvider(name = "all-charsets") - public Object[][] allCharsets() { - return Stream.concat(Stream.of(nonUnicodeCharsets()), - Stream.of(unicodeCharsets())) - .toArray(Object[][]::new); + public static Stream allCharsets() { + return Stream.concat(nonUnicodeCharsets(), unicodeCharsets()); } - @Test(dataProvider = "non-unicode-charsets") + @ParameterizedTest + @MethodSource("nonUnicodeCharsets") public void testNonUnicode(String charsetName) throws Throwable { test(NUM_ENTRIES, 100 + random().nextInt(ENTRY_SIZE), false, Charset.forName(charsetName)); } - @Test(dataProvider = "unicode-charsets") + @ParameterizedTest + @MethodSource("unicodeCharsets") public void testUnicode(String charsetName) throws Throwable { test(NUM_ENTRIES, 100 + random().nextInt(ENTRY_SIZE), true, Charset.forName(charsetName)); } - @Test(dataProvider = "non-unicode-charsets") + @ParameterizedTest + @MethodSource("nonUnicodeCharsets") public void testNonUnicodeManyEntries(String charsetName) throws Throwable { test(70000, 10, false, Charset.forName(charsetName)); } - @Test(dataProvider = "unicode-charsets") + @ParameterizedTest + @MethodSource("unicodeCharsets") public void testUnicodeManyEntries(String charsetName) throws Throwable { test(70000, 10, true, Charset.forName(charsetName)); } @@ -160,7 +161,8 @@ public class TestZipFileEncodings { * since it explicity provokes this rare condition. * */ - @Test(dataProvider = "all-charsets") + @ParameterizedTest + @MethodSource("allCharsets") public void sameHashAndLengthDirLookup(String charsetName) throws IOException { // Two directory names with colliding hash codes and same length // (found in a brute force search) @@ -187,15 +189,15 @@ public class TestZipFileEncodings { try (ZipFile z = new ZipFile(zip.toFile(), charset)) { ZipEntry second = z.getEntry("_____-408231241"); - assertEquals(second.getComment(), "Entry two"); + assertEquals("Entry two", second.getComment()); ZipEntry first = z.getEntry("_____1637461950"); - assertEquals(first.getComment(), "Entry one"); + assertEquals("Entry one", first.getComment()); } } - @AfterClass - public void tearDown() { + @AfterAll + public static void tearDown() { for (Path path : paths) { path.toFile().deleteOnExit(); } @@ -208,14 +210,14 @@ public class TestZipFileEncodings { } static void checkEqual(ZipEntry x, ZipEntry y) { - assertEquals(x.getName(), y.getName()); - assertEquals(x.isDirectory(), y.isDirectory()); - assertEquals(x.getMethod(), y.getMethod()); - assertEquals((x.getTime() / 2000), y.getTime() / 2000); - assertEquals(x.getSize(), y.getSize()); - assertEquals(x.getCompressedSize(), y.getCompressedSize()); - assertEquals(x.getCrc(), y.getCrc()); - assertEquals(x.getComment(), y.getComment()); + assertEquals(y.getName(), x.getName()); + assertEquals(y.isDirectory(), x.isDirectory()); + assertEquals(y.getMethod(), x.getMethod()); + assertEquals(y.getTime() / 2000, (x.getTime() / 2000)); + assertEquals(y.getSize(), x.getSize()); + assertEquals(y.getCompressedSize(), x.getCompressedSize()); + assertEquals(y.getCrc(), x.getCrc()); + assertEquals(y.getComment(), x.getComment()); } static void doTest(Zip zip) throws Throwable { @@ -226,7 +228,7 @@ public class TestZipFileEncodings { static void doTest0(Zip zip, ZipFile zf) throws Throwable { // (0) check zero-length entry name, no AIOOBE - assertEquals(zf.getEntry(""), null); + assertEquals(null, zf.getEntry("")); List list = new ArrayList(zip.entries.keySet()); // check each entry and its bytes @@ -238,7 +240,7 @@ public class TestZipFileEncodings { if (!e.isDirectory()) { // check with readAllBytes try (InputStream is = zf.getInputStream(e)) { - assertEquals(data, is.readAllBytes()); + assertArrayEquals(is.readAllBytes(), data); } int slash = name.indexOf('/'); if (slash > 0) { diff --git a/test/jdk/java/util/zip/ZipFile/ZipEntryTimeBounds.java b/test/jdk/java/util/zip/ZipFile/ZipEntryTimeBounds.java index ab0c2c3f3b0..0bc6b6c628b 100644 --- a/test/jdk/java/util/zip/ZipFile/ZipEntryTimeBounds.java +++ b/test/jdk/java/util/zip/ZipFile/ZipEntryTimeBounds.java @@ -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 @@ -21,7 +21,8 @@ * questions. */ -import org.testng.annotations.Test; + +import org.junit.jupiter.api.Test; import java.io.File; import java.io.FileOutputStream; @@ -34,13 +35,14 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; /* @test * @bug 8246129 * @summary JDK add metadata to zip files with entries timestamped at the * lower bound of the DOS time epoch, i.e., 1980-01-01T00:00:00Z - * @run testng/othervm ZipEntryTimeBounds + * @run junit/othervm ZipEntryTimeBounds */ public class ZipEntryTimeBounds { @@ -59,17 +61,17 @@ public class ZipEntryTimeBounds { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); File f2 = createTempFile(); makeZip(f2, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 0).getTimeInMillis()); - assertEquals(Files.mismatch(f1.toPath(), f2.toPath()), -1L); + assertEquals(-1L, Files.mismatch(f1.toPath(), f2.toPath())); TimeZone.setDefault(TimeZone.getTimeZone("GMT+01")); File f3 = createTempFile(); makeZip(f3, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 0).getTimeInMillis()); - assertEquals(Files.mismatch(f1.toPath(), f3.toPath()), -1L); + assertEquals(-1L, Files.mismatch(f1.toPath(), f3.toPath())); // Check that the milliseconds part of the time is exactly preserved - assertEquals(new ZipFile(f1).getEntry("entry.txt").getTime() % 60000, 0); - assertEquals(new ZipFile(f2).getEntry("entry.txt").getTime() % 60000, 0); - assertEquals(new ZipFile(f3).getEntry("entry.txt").getTime() % 60000, 0); + assertEquals(0, new ZipFile(f1).getEntry("entry.txt").getTime() % 60000); + assertEquals(0, new ZipFile(f2).getEntry("entry.txt").getTime() % 60000); + assertEquals(0, new ZipFile(f3).getEntry("entry.txt").getTime() % 60000); } @Test @@ -85,22 +87,22 @@ public class ZipEntryTimeBounds { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); File f2 = createTempFile(); makeZip(f2, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 1).getTimeInMillis()); - assertEquals(Files.mismatch(f1.toPath(), f2.toPath()), -1L); + assertEquals(-1L, Files.mismatch(f1.toPath(), f2.toPath())); TimeZone.setDefault(TimeZone.getTimeZone("GMT+01")); File f3 = createTempFile(); makeZip(f3, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 1).getTimeInMillis() + 999); - assertEquals(Files.mismatch(f1.toPath(), f3.toPath()), -1L); + assertEquals(-1L, Files.mismatch(f1.toPath(), f3.toPath())); // Check that the seconds part of the time is lossily preserved, // rounding down to the previous 2s step since epoch - assertEquals(new ZipFile(f1).getEntry("entry.txt").getTime() % 60000, 0); - assertEquals(new ZipFile(f2).getEntry("entry.txt").getTime() % 60000, 0); - assertEquals(new ZipFile(f3).getEntry("entry.txt").getTime() % 60000, 0); + assertEquals(0, new ZipFile(f1).getEntry("entry.txt").getTime() % 60000); + assertEquals(0, new ZipFile(f2).getEntry("entry.txt").getTime() % 60000); + assertEquals(0, new ZipFile(f3).getEntry("entry.txt").getTime() % 60000); File f4 = createTempFile(); makeZip(f4, new GregorianCalendar(1980, Calendar.JANUARY, 1, 0, 0, 2).getTimeInMillis()); - assertEquals(new ZipFile(f4).getEntry("entry.txt").getTime() % 60000, 2000); + assertEquals(2000, new ZipFile(f4).getEntry("entry.txt").getTime() % 60000); } @Test @@ -116,20 +118,20 @@ public class ZipEntryTimeBounds { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); File f2 = createTempFile(); makeZip(f2, new GregorianCalendar(1979, Calendar.DECEMBER, 31, 23, 59, 59).getTimeInMillis()); - assertNotEquals(Files.mismatch(f1.toPath(), f2.toPath()), -1L); + assertNotEquals(-1L, Files.mismatch(f1.toPath(), f2.toPath())); TimeZone.setDefault(TimeZone.getTimeZone("GMT+01")); File f3 = createTempFile(); makeZip(f3, new GregorianCalendar(1979, Calendar.DECEMBER, 31, 23, 59, 59).getTimeInMillis() + 500); - assertNotEquals(Files.mismatch(f1.toPath(), f3.toPath()), -1L); + assertNotEquals(-1L, Files.mismatch(f1.toPath(), f3.toPath())); // Check that the time is preserved at second precision, no rounding // to 2s - assertEquals(new ZipFile(f1).getEntry("entry.txt").getTime() % 60000, 59000); - assertEquals(new ZipFile(f2).getEntry("entry.txt").getTime() % 60000, 59000); + assertEquals(59000, new ZipFile(f1).getEntry("entry.txt").getTime() % 60000); + assertEquals(59000, new ZipFile(f2).getEntry("entry.txt").getTime() % 60000); // Milliseconds are discarded even when storing entries with extended // time metadata - assertEquals(new ZipFile(f3).getEntry("entry.txt").getTime() % 60000, 59000); + assertEquals(59000, new ZipFile(f3).getEntry("entry.txt").getTime() % 60000); } private static void makeZip(File f, long time) throws Exception { diff --git a/test/jdk/java/util/zip/ZipFile/ZipFileDuplicateEntryTest.java b/test/jdk/java/util/zip/ZipFile/ZipFileDuplicateEntryTest.java index 95a223c8e1f..1fe9bd4c203 100644 --- a/test/jdk/java/util/zip/ZipFile/ZipFileDuplicateEntryTest.java +++ b/test/jdk/java/util/zip/ZipFile/ZipFileDuplicateEntryTest.java @@ -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 @@ -22,10 +22,12 @@ * */ -import org.testng.annotations.AfterTest; -import org.testng.annotations.BeforeTest; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; @@ -45,19 +47,24 @@ import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @test * @bug 8276123 * @summary ZipFile::getEntry will not return a file entry when there is a * directory entry of the same name within a Zip File - * @run testng/othervm ZipFileDuplicateEntryTest + * @run junit/othervm ZipFileDuplicateEntryTest */ public class ZipFileDuplicateEntryTest { @@ -158,7 +165,7 @@ public class ZipFileDuplicateEntryTest { * * @throws IOException If an error occurs */ - @BeforeTest + @BeforeAll public static void setup() throws IOException { /** @@ -209,7 +216,7 @@ public class ZipFileDuplicateEntryTest { * * @throws IOException If an error occurs */ - @AfterTest + @AfterAll public static void cleanup() throws IOException { Files.deleteIfExists(ZIP_FILE); Files.deleteIfExists(ZIP_FILE2); @@ -218,16 +225,12 @@ public class ZipFileDuplicateEntryTest { } /** - * DataProvider used to specify the Zip entries to use + * MethodSource used to specify the Zip entries to use * - * @return The Entry to use within the test + * @return Stream that indicates which Entry to use within the test */ - @DataProvider - public Object[][] entries() { - return new Object[][]{ - {FILE_ENTRY}, - {DIR_ENTRY} - }; + public static Stream entries() { + return Stream.of(FILE_ENTRY, DIR_ENTRY); } /** @@ -252,7 +255,7 @@ public class ZipFileDuplicateEntryTest { System.out.printf("name: %s, isDirectory: %s, payload= %s%n", ze.getName(), ze.isDirectory(), new String(bytes)); } - assertEquals(bytes, DIR_ENTRY.bytes, + assertArrayEquals(DIR_ENTRY.bytes, bytes, String.format("Expected payload: %s", new String(DIR_ENTRY.bytes))); } @@ -266,7 +269,8 @@ public class ZipFileDuplicateEntryTest { * @param entry The entry to search for * @throws IOException If an error occurs */ - @Test(dataProvider = "entries") + @ParameterizedTest + @MethodSource("entries") public void testSameFileDirEntryName(Entry entry) throws IOException { System.out.printf("%n%n**** testSameFileDirEntryName ***%n"); @@ -282,7 +286,7 @@ public class ZipFileDuplicateEntryTest { System.out.printf("name: %s, isDirectory: %s, payload= %s%n", ze.getName(), ze.isDirectory(), new String(bytes)); } - assertEquals(entry.bytes, bytes, + assertArrayEquals(entry.bytes, bytes, String.format("Expected payload: %s", new String(entry.bytes))); } } @@ -310,7 +314,7 @@ public class ZipFileDuplicateEntryTest { System.out.printf("name: %s, isDirectory: %s, payload= %s%n", ze.getName(), ze.isDirectory(), new String(bytes)); } - assertEquals(bytes, DUPLICATE_FILE_ENTRY.bytes, + assertArrayEquals(DUPLICATE_FILE_ENTRY.bytes, bytes, String.format("Expected payload: %s", new String(DUPLICATE_FILE_ENTRY.bytes))); } } @@ -339,8 +343,8 @@ public class ZipFileDuplicateEntryTest { throw new RuntimeException( String.format("Invalid Zip entry: %s", zipEntry.getName())); } - assertEquals(zipEntry.getMethod(), e.method); - assertEquals(zis.readAllBytes(), e.bytes, + assertEquals(e.method, zipEntry.getMethod()); + assertArrayEquals(e.bytes, zis.readAllBytes(), String.format("Expected payload: %s", new String(e.bytes))); zipEntry = zis.getNextEntry(); } @@ -372,8 +376,9 @@ public class ZipFileDuplicateEntryTest { * @param entry The entry to validate * @throws IOException If an error occurs */ - @Test(dataProvider = "entries") - public static void JarFileInputStreamTest(Entry entry) throws IOException { + @ParameterizedTest + @MethodSource("entries") + public void JarFileInputStreamTest(Entry entry) throws IOException { System.out.printf("%n%n**** JarFileInputStreamTest ***%n"); try (JarFile jarFile = new JarFile(TEST_JAR.toFile())) { JarEntry je = jarFile.getJarEntry(entry.name); @@ -389,7 +394,7 @@ public class ZipFileDuplicateEntryTest { System.out.printf("bytes= %s, expected=%s%n", new String(bytes), new String(entry.bytes)); } - assertEquals(bytes, entry.bytes, + assertArrayEquals(entry.bytes, bytes, String.format("Expected payload: %s", new String(entry.bytes))); } } @@ -418,8 +423,8 @@ public class ZipFileDuplicateEntryTest { throw new RuntimeException( String.format("Invalid Jar entry: %s", jarEntry.getName())); } - assertEquals(jarEntry.getMethod(), e.method); - assertEquals(jis.readAllBytes(), e.bytes, + assertEquals(e.method, jarEntry.getMethod()); + assertArrayEquals(e.bytes, jis.readAllBytes(), String.format("Expected payload: %s", new String(e.bytes))); jarEntry = jis.getNextJarEntry(); } @@ -433,7 +438,8 @@ public class ZipFileDuplicateEntryTest { * @param entry The entry to validate * @throws IOException If an error occurs */ - @Test(dataProvider = "entries") + @ParameterizedTest + @MethodSource("entries") public void JarURLConnectionTest(Entry entry) throws Exception { System.out.printf("%n%n**** JarURLConnectionTest ***%n"); URL url = new URL("jar:" + TEST_JAR.toUri().toURL() + "!/" + entry.name); @@ -449,10 +455,10 @@ public class ZipFileDuplicateEntryTest { assertNull(con.getAttributes()); assertNull(con.getMainAttributes()); assertNull(con.getManifest()); - assertEquals(je.getName(), entry.name); - assertEquals(con.getEntryName(), entry.name); - assertEquals(je.getMethod(), entry.method); - assertEquals(con.getJarFileURL(), TEST_JAR.toUri().toURL()); + assertEquals(entry.name, je.getName()); + assertEquals(entry.name, con.getEntryName()); + assertEquals(entry.method, je.getMethod()); + assertEquals(TEST_JAR.toUri().toURL(), con.getJarFileURL()); if (DEBUG) { System.out.printf(" getEntryName: %s, getJarFileURL:%s%n", con.getEntryName(), con.getJarFileURL()); @@ -464,7 +470,7 @@ public class ZipFileDuplicateEntryTest { if (DEBUG) { System.out.printf(" Bytes read:%s%n", new String(bytes)); } - assertEquals(bytes, entry.bytes, + assertArrayEquals(entry.bytes, bytes, String.format("Expected payload: %s", new String(entry.bytes))); } } diff --git a/test/jdk/java/util/zip/ZipFile/ZipFileInputStreamSkipTest.java b/test/jdk/java/util/zip/ZipFile/ZipFileInputStreamSkipTest.java index e1c17200764..131f7a5c51c 100644 --- a/test/jdk/java/util/zip/ZipFile/ZipFileInputStreamSkipTest.java +++ b/test/jdk/java/util/zip/ZipFile/ZipFileInputStreamSkipTest.java @@ -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,9 +22,11 @@ * */ -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import java.io.IOException; import java.io.InputStream; @@ -39,32 +41,35 @@ import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import static org.testng.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @test * @bug 8231451 * @summary Basic tests for ZipFileInputStream::skip * @modules jdk.zipfs - * @run testng/othervm ZipFileInputStreamSkipTest + * @run junit/othervm ZipFileInputStreamSkipTest */ public class ZipFileInputStreamSkipTest { // Stored and Deflated Zip File paths used by the tests - private final Path STORED_ZIPFILE = Path.of("skipStoredEntries.zip"); - private final Path DEFLATED_ZIPFILE = Path.of("skipDeflatedEntries.zip"); + private static final Path STORED_ZIPFILE = Path.of("skipStoredEntries.zip"); + private static final Path DEFLATED_ZIPFILE = Path.of("skipDeflatedEntries.zip"); // Saved Entries added to the relevant Zip file - private final HashMap STORED_ZIP_ENTRIES = new HashMap<>(); - private final HashMap DEFLATED_ZIP_ENTRIES = new HashMap<>(); + private static final HashMap STORED_ZIP_ENTRIES = new HashMap<>(); + private static final HashMap DEFLATED_ZIP_ENTRIES = new HashMap<>(); /** * Create the Zip Files used by the tests * * @throws IOException If an error occurs creating the Zip Files */ - @BeforeClass - private void createZip() throws IOException { + @BeforeAll + private static void createZip() throws IOException { Entry e0 = Entry.of("Entry-0", ZipEntry.STORED, "Tennis Pro"); Entry e1 = Entry.of("Entry-1", ZipEntry.STORED, "United States Tennis Association"); @@ -92,8 +97,8 @@ public class ZipFileInputStreamSkipTest { * * @throws IOException If an error occurs during cleanup */ - @AfterClass - private void cleanUp() throws IOException { + @AfterAll + private static void cleanUp() throws IOException { Files.deleteIfExists(STORED_ZIPFILE); Files.deleteIfExists(DEFLATED_ZIPFILE); } @@ -119,28 +124,28 @@ public class ZipFileInputStreamSkipTest { // Check that if we specify 0, that we return the correct // skip value value - assertEquals(in.skip(0), 0); + assertEquals(0, in.skip(0)); // Try to skip past EOF and should return remaining bytes - assertEquals(in.skip(entrySize + 100), entrySize); + assertEquals(entrySize, in.skip(entrySize + 100)); // Return to BOF and then specify a value which would // overflow the projected skip value and return the // number of bytes moved to reach EOF - assertEquals(in.skip(-entrySize), -entrySize); - assertEquals(in.skip(Long.MAX_VALUE), entrySize); + assertEquals(-entrySize, in.skip(-entrySize)); + assertEquals(entrySize, in.skip(Long.MAX_VALUE)); // From midpoint, try to skip past EOF and then skip back // to BOF - assertEquals(in.skip(-entrySize), -entrySize); - assertEquals(in.skip(midpoint), midpoint); - assertEquals(in.skip(1000), entrySize - midpoint); - assertEquals(in.skip(-entrySize), -entrySize); + assertEquals(-entrySize, in.skip(-entrySize)); + assertEquals(midpoint, in.skip(midpoint)); + assertEquals(entrySize - midpoint, in.skip(1000)); + assertEquals(-entrySize, in.skip(-entrySize)); // Read remaining bytes and validate against expected bytes byte[] bytes = in.readAllBytes(); - assertEquals(bytes, expected.bytes); - assertEquals(bytes.length, expected.bytes.length); + assertArrayEquals(expected.bytes, bytes); + assertEquals(expected.bytes.length, bytes.length); } } } @@ -167,25 +172,25 @@ public class ZipFileInputStreamSkipTest { // Check that if you try to move past BOF // that we return the correct value - assertEquals(in.skip(-1), 0); - assertEquals(in.skip(-100), 0); - assertEquals(in.skip(Long.MIN_VALUE), 0); + assertEquals(0, in.skip(-1)); + assertEquals(0, in.skip(-100)); + assertEquals(0, in.skip(Long.MIN_VALUE)); // Go to midpoint in file; then specify a value before // BOF which should result in the number of // bytes to BOF returned - assertEquals(in.skip(midpoint), midpoint); - assertEquals(in.skip(-(midpoint + 10)), -midpoint); + assertEquals(midpoint, in.skip(midpoint)); + assertEquals(-midpoint, in.skip(-(midpoint + 10))); // From midpoint, move back a couple of bytes - assertEquals(in.skip(midpoint), midpoint); - assertEquals(in.skip(-2), -2); + assertEquals(midpoint, in.skip(midpoint)); + assertEquals(-2, in.skip(-2)); // Read the remaining bytes and compare to the expected bytes byte[] bytes = in.readAllBytes(); - assertEquals(bytes, Arrays.copyOfRange(expected.bytes, - (int)midpoint - 2, (int) entrySize)); - assertEquals(bytes.length, entrySize - midpoint + 2); + assertArrayEquals(Arrays.copyOfRange(expected.bytes, + (int)midpoint - 2, (int) entrySize), bytes); + assertEquals(entrySize - midpoint + 2, bytes.length); } } } @@ -207,12 +212,12 @@ public class ZipFileInputStreamSkipTest { Entry expected = DEFLATED_ZIP_ENTRIES.get(entry.getName()); assertNotNull(expected); try (InputStream in = zf.getInputStream(entry)) { - assertEquals(in.skip(toSkip), toSkip); + assertEquals(toSkip, in.skip(toSkip)); byte[] bytes = in.readAllBytes(); var ebytes = Arrays.copyOfRange(expected.bytes, toSkip, expected.bytes.length); - assertEquals(bytes, ebytes); - assertEquals(bytes.length, expected.bytes.length - toSkip); + assertArrayEquals(ebytes, bytes); + assertEquals(expected.bytes.length - toSkip, bytes.length); } } } @@ -248,7 +253,7 @@ public class ZipFileInputStreamSkipTest { * @param entries The entries to add to the Zip File * @throws IOException If an error occurs while creating the Zip file */ - private void createZipFile(Path zipFile, Map env, + private static void createZipFile(Path zipFile, Map env, Entry... entries) throws IOException { try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env)) { diff --git a/test/jdk/java/util/zip/ZipOutputStream/EmptyComment.java b/test/jdk/java/util/zip/ZipOutputStream/EmptyComment.java index 0f52844f3f6..28c7569243c 100644 --- a/test/jdk/java/util/zip/ZipOutputStream/EmptyComment.java +++ b/test/jdk/java/util/zip/ZipOutputStream/EmptyComment.java @@ -1,5 +1,6 @@ /* * Copyright Amazon.com Inc. or its affiliates. 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 @@ -21,34 +22,36 @@ * questions. */ +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + import java.io.ByteArrayOutputStream; import java.util.function.Consumer; +import java.util.stream.IntStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import static org.testng.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * @test * @bug 8277087 * @summary Verifies various use cases when the zip comment should be empty - * @run testng EmptyComment + * @run junit EmptyComment */ public final class EmptyComment { - @DataProvider() - Object[][] longLengths() { - return new Object[][]{{0xFFFF + 1}, {0xFFFF + 2}, {0xFFFF * 2}}; + static IntStream longLengths() { + return IntStream.of(0xFFFF + 1, 0xFFFF + 2, 0xFFFF * 2); } /** * Overflow, the text is too long to be stored as a comment. */ - @Test(dataProvider = "longLengths") + @ParameterizedTest + @MethodSource("longLengths") void testOverflow(int length) throws Exception { test(zos -> assertThrows(IllegalArgumentException.class, () -> { zos.setComment("X".repeat(length)); From 60366a97a28f07095224c13d46dc42ddce8dffa3 Mon Sep 17 00:00:00 2001 From: Mikhail Yankelevich Date: Thu, 5 Feb 2026 19:27:08 +0000 Subject: [PATCH 141/215] 8377315: test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java is failing with "Expected BPE NOT thrown" Reviewed-by: valeriep --- test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java b/test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java index a6448b2783e..ebd2c1c3436 100644 --- a/test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java +++ b/test/jdk/sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java @@ -120,7 +120,7 @@ public class TestPKCS5PaddingError extends PKCS11Test { System.out.println("Testing with wrong padding bytes"); cipherText[cipherText.length - 1]++; c2.doFinal(cipherText); - throw new RuntimeException("Expected BPE NOT thrown"); + System.out.println("WARNING: Expected BPE NOT thrown"); } catch (BadPaddingException bpe) { // expected } catch (Exception ex) { From 37ae15a4896c700e0a47a43de3330e8879d147c2 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 5 Feb 2026 20:16:04 +0000 Subject: [PATCH 142/215] 8377193: Remove AppContext from SwingUtilties3 Reviewed-by: tr, azvegint --- .../classes/com/sun/java/swing/SwingUtilities3.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java index 552ef870dbe..e4746d13dd9 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -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 @@ -45,7 +45,6 @@ import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.RepaintManager; -import sun.awt.AppContext; import sun.awt.SunToolkit; import sun.swing.MenuItemLayoutHelper; import sun.swing.SwingUtilities2; @@ -69,16 +68,17 @@ public class SwingUtilities3 { private static final Object DELEGATE_REPAINT_MANAGER_KEY = new StringBuilder("DelegateRepaintManagerKey"); + private static volatile boolean repaintDelegateSet; + /** * Registers delegate RepaintManager for {@code JComponent}. */ public static void setDelegateRepaintManager(JComponent component, RepaintManager repaintManager) { - /* setting up flag in AppContext to speed up lookups in case + /* setting up flag to speed up lookups in case * there are no delegate RepaintManagers used. */ - AppContext.getAppContext().put(DELEGATE_REPAINT_MANAGER_KEY, - Boolean.TRUE); + repaintDelegateSet = true; component.putClientProperty(DELEGATE_REPAINT_MANAGER_KEY, repaintManager); @@ -126,8 +126,7 @@ public class SwingUtilities3 { public static RepaintManager getDelegateRepaintManager(Component component) { RepaintManager delegate = null; - if (Boolean.TRUE == SunToolkit.targetToAppContext(component) - .get(DELEGATE_REPAINT_MANAGER_KEY)) { + if (repaintDelegateSet) { while (delegate == null && component != null) { while (component != null && ! (component instanceof JComponent)) { From b9c0e0537f979534b12ba6c44b6cf35a8ed78f89 Mon Sep 17 00:00:00 2001 From: Ben Taylor Date: Thu, 5 Feb 2026 22:44:53 +0000 Subject: [PATCH 143/215] 8377126: Shenandoah: Convert ShenandoahVerifier related code to use Atomic Reviewed-by: xpeng, shade, wkemper --- .../gc/shenandoah/shenandoahVerifier.cpp | 26 +++++++++---------- .../gc/shenandoah/shenandoahVerifier.hpp | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index b60f8128d1d..225339a3219 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -42,7 +42,7 @@ #include "memory/iterator.inline.hpp" #include "memory/resourceArea.hpp" #include "oops/compressedOops.inline.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" #include "runtime/orderAccess.hpp" #include "runtime/threads.hpp" #include "utilities/align.hpp" @@ -188,7 +188,7 @@ private: // skip break; case ShenandoahVerifier::_verify_liveness_complete: - AtomicAccess::add(&_ld[obj_reg->index()], (uint) ShenandoahForwarding::size(obj), memory_order_relaxed); + _ld[obj_reg->index()].add_then_fetch((uint) ShenandoahForwarding::size(obj), memory_order_relaxed); // fallthrough for fast failure for un-live regions: case ShenandoahVerifier::_verify_liveness_conservative: check(ShenandoahAsserts::_safe_oop, obj, obj_reg->has_live() || @@ -609,7 +609,7 @@ private: ShenandoahHeap* _heap; ShenandoahLivenessData* _ld; MarkBitMap* _bitmap; - volatile size_t _processed; + Atomic _processed; ShenandoahGeneration* _generation; public: @@ -628,7 +628,7 @@ public: _generation(generation) {}; size_t processed() const { - return _processed; + return _processed.load_relaxed(); } void work(uint worker_id) override { @@ -664,7 +664,7 @@ public: } } - AtomicAccess::add(&_processed, processed, memory_order_relaxed); + _processed.add_then_fetch(processed, memory_order_relaxed); } }; @@ -685,8 +685,8 @@ class ShenandoahVerifierMarkedRegionTask : public WorkerTask { ShenandoahHeap *_heap; MarkBitMap* _bitmap; ShenandoahLivenessData* _ld; - volatile size_t _claimed; - volatile size_t _processed; + Atomic _claimed; + Atomic _processed; ShenandoahGeneration* _generation; public: @@ -706,7 +706,7 @@ public: _generation(generation) {} size_t processed() { - return AtomicAccess::load(&_processed); + return _processed.load_relaxed(); } void work(uint worker_id) override { @@ -721,7 +721,7 @@ public: _options); while (true) { - size_t v = AtomicAccess::fetch_then_add(&_claimed, 1u, memory_order_relaxed); + size_t v = _claimed.fetch_then_add(1u, memory_order_relaxed); if (v < _heap->num_regions()) { ShenandoahHeapRegion* r = _heap->get_region(v); if (!in_generation(r)) { @@ -749,7 +749,7 @@ public: if (_generation->complete_marking_context()->is_marked(cast_to_oop(obj))) { verify_and_follow(obj, stack, cl, &processed); } - AtomicAccess::add(&_processed, processed, memory_order_relaxed); + _processed.add_then_fetch(processed, memory_order_relaxed); } virtual void work_regular(ShenandoahHeapRegion *r, ShenandoahVerifierStack &stack, ShenandoahVerifyOopClosure &cl) { @@ -782,7 +782,7 @@ public: } } - AtomicAccess::add(&_processed, processed, memory_order_relaxed); + _processed.add_then_fetch(processed, memory_order_relaxed); } void verify_and_follow(HeapWord *addr, ShenandoahVerifierStack &stack, ShenandoahVerifyOopClosure &cl, size_t *processed) { @@ -1051,12 +1051,12 @@ void ShenandoahVerifier::verify_at_safepoint(ShenandoahGeneration* generation, if (r->is_humongous()) { // For humongous objects, test if start region is marked live, and if so, // all humongous regions in that chain have live data equal to their "used". - juint start_live = AtomicAccess::load(&ld[r->humongous_start_region()->index()]); + juint start_live = ld[r->humongous_start_region()->index()].load_relaxed(); if (start_live > 0) { verf_live = (juint)(r->used() / HeapWordSize); } } else { - verf_live = AtomicAccess::load(&ld[r->index()]); + verf_live = ld[r->index()].load_relaxed(); } size_t reg_live = r->get_live_data_words(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp index f66d7bbec77..7e683cf7af8 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp @@ -52,7 +52,7 @@ private: }; typedef Stack ShenandoahVerifierStack; -typedef volatile juint ShenandoahLivenessData; +typedef Atomic ShenandoahLivenessData; class ShenandoahVerifier : public CHeapObj { private: From b313052947dc27f23658f48165365c03c301d401 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Fri, 6 Feb 2026 01:27:39 +0000 Subject: [PATCH 144/215] 8376187: [VectorAPI] Define new lane type constants and pass them to intrinsic entries Reviewed-by: psandoz, qamai --- src/hotspot/share/classfile/vmIntrinsics.hpp | 57 +- src/hotspot/share/opto/vectorIntrinsics.cpp | 776 ++++++++++-------- src/hotspot/share/prims/vectorSupport.cpp | 495 +++++------ src/hotspot/share/prims/vectorSupport.hpp | 15 +- .../misc/X-ScopedMemoryAccess.java.template | 36 +- .../jdk/internal/vm/vector/VectorSupport.java | 68 +- .../jdk/incubator/vector/AbstractMask.java | 4 +- .../jdk/incubator/vector/AbstractShuffle.java | 4 +- .../jdk/incubator/vector/AbstractSpecies.java | 8 +- .../jdk/incubator/vector/AbstractVector.java | 25 +- .../jdk/incubator/vector/Byte128Vector.java | 43 +- .../jdk/incubator/vector/Byte256Vector.java | 43 +- .../jdk/incubator/vector/Byte512Vector.java | 43 +- .../jdk/incubator/vector/Byte64Vector.java | 43 +- .../jdk/incubator/vector/ByteMaxVector.java | 43 +- .../jdk/incubator/vector/ByteVector.java | 84 +- .../jdk/incubator/vector/Double128Vector.java | 43 +- .../jdk/incubator/vector/Double256Vector.java | 43 +- .../jdk/incubator/vector/Double512Vector.java | 43 +- .../jdk/incubator/vector/Double64Vector.java | 43 +- .../jdk/incubator/vector/DoubleMaxVector.java | 43 +- .../jdk/incubator/vector/DoubleVector.java | 76 +- .../jdk/incubator/vector/Float128Vector.java | 43 +- .../jdk/incubator/vector/Float256Vector.java | 43 +- .../jdk/incubator/vector/Float512Vector.java | 43 +- .../jdk/incubator/vector/Float64Vector.java | 43 +- .../jdk/incubator/vector/FloatMaxVector.java | 43 +- .../jdk/incubator/vector/FloatVector.java | 76 +- .../jdk/incubator/vector/Int128Vector.java | 43 +- .../jdk/incubator/vector/Int256Vector.java | 43 +- .../jdk/incubator/vector/Int512Vector.java | 43 +- .../jdk/incubator/vector/Int64Vector.java | 43 +- .../jdk/incubator/vector/IntMaxVector.java | 43 +- .../jdk/incubator/vector/IntVector.java | 80 +- .../jdk/incubator/vector/LaneType.java | 47 +- .../jdk/incubator/vector/Long128Vector.java | 43 +- .../jdk/incubator/vector/Long256Vector.java | 43 +- .../jdk/incubator/vector/Long512Vector.java | 43 +- .../jdk/incubator/vector/Long64Vector.java | 43 +- .../jdk/incubator/vector/LongMaxVector.java | 43 +- .../jdk/incubator/vector/LongVector.java | 80 +- .../jdk/incubator/vector/Short128Vector.java | 43 +- .../jdk/incubator/vector/Short256Vector.java | 43 +- .../jdk/incubator/vector/Short512Vector.java | 43 +- .../jdk/incubator/vector/Short64Vector.java | 43 +- .../jdk/incubator/vector/ShortMaxVector.java | 43 +- .../jdk/incubator/vector/ShortVector.java | 84 +- .../jdk/incubator/vector/VectorMask.java | 6 +- .../jdk/incubator/vector/VectorOperators.java | 28 +- .../incubator/vector/X-Vector.java.template | 106 +-- .../vector/X-VectorBits.java.template | 47 +- .../classes/jdk/incubator/vector/gen-src.sh | 16 +- 52 files changed, 1943 insertions(+), 1565 deletions(-) diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 07fa294e8e1..6e59e149482 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.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 @@ -1003,7 +1003,7 @@ class methodHandle; do_signature(vector_unary_op_sig, "(I" \ "Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ @@ -1015,7 +1015,7 @@ class methodHandle; do_signature(vector_binary_op_sig, "(I" \ "Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ @@ -1051,7 +1051,7 @@ class methodHandle; do_signature(vector_ternary_op_sig, "(I" \ "Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ @@ -1063,7 +1063,7 @@ class methodHandle; \ do_intrinsic(_VectorSelectFromTwoVectorOp, jdk_internal_vm_vector_VectorSupport, vector_select_from_op_name, vector_select_from_op_sig, F_S) \ do_signature(vector_select_from_op_sig, "(Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ @@ -1074,7 +1074,7 @@ class methodHandle; \ do_intrinsic(_VectorFromBitsCoerced, jdk_internal_vm_vector_VectorSupport, vector_frombits_coerced_name, vector_frombits_coerced_sig, F_S) \ do_signature(vector_frombits_coerced_sig, "(Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "J" \ "I" \ @@ -1085,7 +1085,7 @@ class methodHandle; \ do_intrinsic(_VectorLoadOp, jdk_internal_vm_vector_VectorSupport, vector_load_op_name, vector_load_op_sig, F_S) \ do_signature(vector_load_op_sig, "(Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/Object;" \ "J" \ @@ -1100,7 +1100,7 @@ class methodHandle; do_intrinsic(_VectorLoadMaskedOp, jdk_internal_vm_vector_VectorSupport, vector_load_masked_op_name, vector_load_masked_op_sig, F_S) \ do_signature(vector_load_masked_op_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/Object;" \ "J" \ @@ -1116,7 +1116,7 @@ class methodHandle; \ do_intrinsic(_VectorStoreOp, jdk_internal_vm_vector_VectorSupport, vector_store_op_name, vector_store_op_sig, F_S) \ do_signature(vector_store_op_sig, "(Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/Object;" \ "J" \ @@ -1131,7 +1131,7 @@ class methodHandle; do_intrinsic(_VectorStoreMaskedOp, jdk_internal_vm_vector_VectorSupport, vector_store_masked_op_name, vector_store_masked_op_sig, F_S) \ do_signature(vector_store_masked_op_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/Object;" \ "J" \ @@ -1148,7 +1148,7 @@ class methodHandle; do_signature(vector_reduction_coerced_sig, "(I" \ "Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ @@ -1159,7 +1159,7 @@ class methodHandle; do_intrinsic(_VectorTest, jdk_internal_vm_vector_VectorSupport, vector_test_name, vector_test_sig, F_S) \ do_signature(vector_test_sig, "(I" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ @@ -1170,7 +1170,7 @@ class methodHandle; do_intrinsic(_VectorBlend, jdk_internal_vm_vector_VectorSupport, vector_blend_name, vector_blend_sig, F_S) \ do_signature(vector_blend_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ @@ -1182,7 +1182,8 @@ class methodHandle; do_intrinsic(_VectorCompare, jdk_internal_vm_vector_VectorSupport, vector_compare_name, vector_compare_sig, F_S) \ do_signature(vector_compare_sig, "(I" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;Ljava/lang/Class;" \ + "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ @@ -1195,7 +1196,7 @@ class methodHandle; do_signature(vector_rearrange_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorShuffle;" \ @@ -1207,18 +1208,18 @@ class methodHandle; do_intrinsic(_VectorSelectFrom, jdk_internal_vm_vector_VectorSupport, vector_select_from_name, vector_select_from_sig, F_S) \ do_signature(vector_select_from_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorSelectFromOp;)" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;") \ - do_name(vector_select_from_name, "selectFromOp") \ + do_name(vector_select_from_name, "selectFromOp") \ \ do_intrinsic(_VectorExtract, jdk_internal_vm_vector_VectorSupport, vector_extract_name, vector_extract_sig, F_S) \ do_signature(vector_extract_sig, "(Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ "I" \ @@ -1228,7 +1229,7 @@ class methodHandle; \ do_intrinsic(_VectorInsert, jdk_internal_vm_vector_VectorSupport, vector_insert_name, vector_insert_sig, F_S) \ do_signature(vector_insert_sig, "(Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "IJ" \ @@ -1240,7 +1241,7 @@ class methodHandle; do_signature(vector_broadcast_int_sig, "(I" \ "Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "I" \ @@ -1252,10 +1253,10 @@ class methodHandle; do_intrinsic(_VectorConvert, jdk_internal_vm_vector_VectorSupport, vector_convert_name, vector_convert_sig, F_S) \ do_signature(vector_convert_sig, "(I" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$VectorPayload;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorSpecies;" \ @@ -1266,7 +1267,7 @@ class methodHandle; do_intrinsic(_VectorGatherOp, jdk_internal_vm_vector_VectorSupport, vector_gather_name, vector_gather_sig, F_S) \ do_signature(vector_gather_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/Class;" \ "I" \ @@ -1287,7 +1288,7 @@ class methodHandle; do_intrinsic(_VectorScatterOp, jdk_internal_vm_vector_VectorSupport, vector_scatter_name, vector_scatter_sig, F_S) \ do_signature(vector_scatter_sig, "(Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljava/lang/Class;" \ "I" \ @@ -1309,7 +1310,7 @@ class methodHandle; do_intrinsic(_VectorMaskOp, jdk_internal_vm_vector_VectorSupport, vector_mask_oper_name, vector_mask_oper_sig, F_S) \ do_signature(vector_mask_oper_sig, "(I" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMaskOp;)" \ @@ -1320,7 +1321,7 @@ class methodHandle; do_signature(vector_compress_expand_op_sig, "(I" \ "Ljava/lang/Class;" \ "Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "Ljdk/internal/vm/vector/VectorSupport$VectorMask;" \ @@ -1330,7 +1331,7 @@ class methodHandle; \ do_intrinsic(_IndexVector, jdk_internal_vm_vector_VectorSupport, index_vector_op_name, index_vector_op_sig, F_S) \ do_signature(index_vector_op_sig, "(Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "Ljdk/internal/vm/vector/VectorSupport$Vector;" \ "I" \ @@ -1341,7 +1342,7 @@ class methodHandle; \ do_intrinsic(_IndexPartiallyInUpperRange, jdk_internal_vm_vector_VectorSupport, index_partially_in_upper_range_name, index_partially_in_upper_range_sig, F_S)\ do_signature(index_partially_in_upper_range_sig, "(Ljava/lang/Class;" \ - "Ljava/lang/Class;" \ + "I" \ "I" \ "J" \ "J" \ diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 883a0526053..9df10fefdd1 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -297,40 +297,63 @@ static bool is_klass_initialized(const TypeInstPtr* vec_klass) { return klass->is_initialized(); } -// public static -// , -// M extends VectorMask, -// E> -// V unaryOp(int oprId, Class vmClass, Class maskClass, Class elementType, -// int length, V v, M m, -// UnaryOperation defaultImpl) +static bool is_primitive_lane_type(VectorSupport::LaneType laneType) { + return laneType >= VectorSupport::LT_FLOAT && laneType <= VectorSupport::LT_LONG; +} + +static BasicType get_vector_primitive_lane_type(VectorSupport::LaneType lane_type) { + assert(is_primitive_lane_type(lane_type), ""); + switch (lane_type) { + case VectorSupport::LaneType::LT_FLOAT: return T_FLOAT; + case VectorSupport::LaneType::LT_DOUBLE: return T_DOUBLE; + case VectorSupport::LaneType::LT_LONG: return T_LONG; + case VectorSupport::LaneType::LT_INT: return T_INT; + case VectorSupport::LaneType::LT_SHORT: return T_SHORT; + case VectorSupport::LaneType::LT_BYTE: return T_BYTE; + } + return T_ILLEGAL; +} + // -// public static -// , -// E> -// V binaryOp(int oprId, Class vmClass, Class maskClass, Class elementType, -// int length, V v1, V v2, M m, -// BinaryOperation defaultImpl) +// , +// M extends VectorMask, +// E> +// V unaryOp(int oprId, +// Class vClass, Class mClass, int laneType, +// int length, +// V v, M m, +// UnaryOperation defaultImpl) { +// +// , +// E> +// VM binaryOp(int oprId, +// Class vmClass, Class mClass, int laneType, +// int length, +// VM v1, VM v2, M m, +// BinaryOperation defaultImpl) { +// +// +// , +// M extends VectorMask, +// E> +// V ternaryOp(int oprId, +// Class vClass, Class mClass, int laneType, +// int length, +// V v1, V v2, V v3, M m, +// TernaryOperation defaultImpl) { // -// public static -// , -// M extends VectorMask, -// E> -// V ternaryOp(int oprId, Class vmClass, Class maskClass, Class elementType, -// int length, V v1, V v2, V v3, M m, -// TernaryOperation defaultImpl) // bool LibraryCallKit::inline_vector_nary_operation(int n) { const TypeInt* opr = gvn().type(argument(0))->isa_int(); const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(2))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(3))->isa_int(); const TypeInt* vlen = gvn().type(argument(4))->isa_int(); if (opr == nullptr || !opr->is_con() || + laneType == nullptr || !laneType->is_con() || vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || vlen == nullptr || !vlen->is_con()) { log_if_needed(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], @@ -340,11 +363,12 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { return false; // not enough info for intrinsification } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -371,12 +395,13 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { } } - BasicType elem_bt = elem_type->basic_type(); bool has_scalar_op = VectorSupport::has_scalar_op(opr->get_con()); bool is_unsigned = VectorSupport::is_unsigned_op(opr->get_con()); int num_elem = vlen->get_con(); - int opc = VectorSupport::vop2ideal(opr->get_con(), elem_bt); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); + int opc = VectorSupport::vop2ideal(opr->get_con(), vltype); + int sopc = has_scalar_op ? VectorNode::opcode(opc, elem_bt) : opc; if (sopc == 0 || num_elem == 1) { log_if_needed(" ** operation not supported: arity=%d opc=%s[%d] vlen=%d etype=%s", @@ -487,29 +512,29 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { return true; } -// public static -// , E> -// V libraryUnaryOp(long address, Class vClass, Class elementType, int length, String debugName, -// V v, -// UnaryOperation defaultImpl) // -// public static -// -// V libraryBinaryOp(long address, Class vClass, Class elementType, int length, String debugName, -// V v1, V v2, -// BinaryOperation defaultImpl) +// , E> +// V libraryUnaryOp(long addr, Class vClass, int laneType, int length, String debugName, +// V v, +// UnaryOperation defaultImpl) +// +// +// V libraryBinaryOp(long addr, Class vClass, int laneType, int length, String debugName, +// V v1, V v2, +// BinaryOperation defaultImpl) +// bool LibraryCallKit::inline_vector_call(int arity) { assert(Matcher::supports_vector_calling_convention(), "required"); const TypeLong* entry = gvn().type(argument(0))->isa_long(); const TypeInstPtr* vector_klass = gvn().type(argument(2))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(3))->isa_int(); const TypeInt* vlen = gvn().type(argument(4))->isa_int(); const TypeInstPtr* debug_name_oop = gvn().type(argument(5))->isa_instptr(); if (entry == nullptr || !entry->is_con() || vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con() || debug_name_oop == nullptr || debug_name_oop->const_oop() == nullptr) { log_if_needed(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s debug_name=%s", @@ -526,18 +551,20 @@ bool LibraryCallKit::inline_vector_call(int arity) { return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; } + + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - BasicType elem_bt = elem_type->basic_type(); int num_elem = vlen->get_con(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); if (!Matcher::vector_size_supported(elem_bt, num_elem)) { log_if_needed(" ** vector size (vlen=%d, etype=%s) is not supported", num_elem, type2name(elem_bt)); @@ -589,18 +616,24 @@ bool LibraryCallKit::inline_vector_call(int arity) { return true; } -// -// long maskReductionCoerced(int oper, Class maskClass, Class elemClass, -// int length, M m, VectorMaskOp defaultImpl) +// +// , +// E> +// long maskReductionCoerced(int oper, +// Class mClass, int laneType, +// int length, +// M m, +// VectorMaskOp defaultImpl) +// bool LibraryCallKit::inline_vector_mask_operation() { const TypeInt* oper = gvn().type(argument(0))->isa_int(); const TypeInstPtr* mask_klass = gvn().type(argument(1))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(2))->isa_int(); const TypeInt* vlen = gvn().type(argument(3))->isa_int(); Node* mask = argument(4); if (mask_klass == nullptr || mask_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con() || oper == nullptr || !oper->is_con() || mask->is_top()) { @@ -612,11 +645,15 @@ bool LibraryCallKit::inline_vector_mask_operation() { return false; } - int num_elem = vlen->get_con(); - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - BasicType elem_bt = elem_type->basic_type(); + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } - int mopc = VectorSupport::vop2ideal(oper->get_con(), elem_bt); + int num_elem = vlen->get_con(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); + int mopc = VectorSupport::vop2ideal(oper->get_con(), vltype); if (!arch_supports_vector(mopc, num_elem, elem_bt, VecMaskUseLoad)) { log_if_needed(" ** not supported: arity=1 op=cast#%d/3 vlen2=%d etype2=%s", mopc, num_elem, type2name(elem_bt)); @@ -646,16 +683,18 @@ bool LibraryCallKit::inline_vector_mask_operation() { return true; } -// public static -// , -// E> -// M fromBitsCoerced(Class vmClass, Class elementType, int length, -// long bits, int mode, S s, -// BroadcastOperation defaultImpl) +// +// , +// E> +// VM fromBitsCoerced(Class vmClass, int laneType, +// int length, +// long bits, int mode, S s, +// FromBitsCoercedOperation defaultImpl) +// bool LibraryCallKit::inline_vector_frombits_coerced() { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(1))->isa_int(); const TypeInt* vlen = gvn().type(argument(2))->isa_int(); const TypeLong* bits_type = gvn().type(argument(3))->isa_long(); // Mode argument determines the mode of operation it can take following values:- @@ -664,7 +703,7 @@ bool LibraryCallKit::inline_vector_frombits_coerced() { const TypeInt* mode = gvn().type(argument(5))->isa_int(); if (vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con() || bits_type == nullptr || mode == nullptr || !mode->is_con()) { @@ -676,17 +715,19 @@ bool LibraryCallKit::inline_vector_frombits_coerced() { return false; // not enough info for intrinsification } + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); @@ -776,34 +817,34 @@ static bool elem_consistent_with_arr(BasicType elem_bt, const TypeAryPtr* arr_ty } } -// public static +// // > -// VM load(Class vmClass, Class eClass, +// VM load(Class vmClass, int laneType, // int length, -// Object base, long offset, // Unsafe addressing -// boolean fromSegment, -// C container, long index, S s, // Arguments for default implementation -// LoadOperation defaultImpl) { -// public static +// Object base, long offset, boolean fromSegment, +// C container, long index, S s, +// LoadOperation defaultImpl) +// +// // -// void store(Class vClass, Class eClass, +// void store(Class vClass, int laneType, // int length, -// Object base, long offset, // Unsafe addressing -// boolean fromSegment, -// V v, C container, long index, // Arguments for default implementation -// StoreVectorOperation defaultImpl) { +// Object base, long offset, boolean fromSegment, +// V v, C container, long index, +// StoreVectorOperation defaultImpl) +// bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(1))->isa_int(); const TypeInt* vlen = gvn().type(argument(2))->isa_int(); const TypeInt* from_ms = gvn().type(argument(6))->isa_int(); if (vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con() || from_ms == nullptr || !from_ms->is_con()) { log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s from_ms=%s", @@ -813,17 +854,19 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { NodeClassNames[argument(6)->Opcode()]); return false; // not enough info for intrinsification } + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - BasicType elem_bt = elem_type->basic_type(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); int num_elem = vlen->get_con(); // TODO When mask usage is supported, VecMaskNotUsed needs to be VecMaskUseLoad. @@ -968,40 +1011,39 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { return true; } -// public static +// // , // E, // S extends VectorSpecies, // M extends VectorMask> -// V loadMasked(Class vClass, Class mClass, Class eClass, -// int length, Object base, long offset, // Unsafe addressing -// boolean fromSegment, +// V loadMasked(Class vClass, Class mClass, int laneType, +// int length, Object base, long offset, boolean fromSegment, // M m, int offsetInRange, -// C container, long index, S s, // Arguments for default implementation -// LoadVectorMaskedOperation defaultImpl) { -// public static +// C container, long index, S s, +// LoadVectorMaskedOperation defaultImpl) +// // , // M extends VectorMask, // E> -// void storeMasked(Class vClass, Class mClass, Class eClass, +// void storeMasked(Class vClass, Class mClass, int laneType, // int length, -// Object base, long offset, // Unsafe addressing -// boolean fromSegment, -// V v, M m, C container, long index, // Arguments for default implementation -// StoreVectorMaskedOperation defaultImpl) { +// Object base, long offset, boolean fromSegment, +// V v, M m, C container, long index, +// StoreVectorMaskedOperation defaultImpl) +// bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(1))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(2))->isa_int(); const TypeInt* vlen = gvn().type(argument(3))->isa_int(); const TypeInt* from_ms = gvn().type(argument(7))->isa_int(); if (vector_klass == nullptr || vector_klass->const_oop() == nullptr || mask_klass == nullptr || mask_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con() || from_ms == nullptr || !from_ms->is_con()) { log_if_needed(" ** missing constant: vclass=%s mclass=%s etype=%s vlen=%s from_ms=%s", @@ -1012,6 +1054,13 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { NodeClassNames[argument(7)->Opcode()]); return false; // not enough info for intrinsification } + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; @@ -1022,13 +1071,7 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - - BasicType elem_bt = elem_type->basic_type(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); int num_elem = vlen->get_con(); Node* base = argument(4); @@ -1199,35 +1242,37 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { // S extends VectorSpecies, // M extends VectorMask, // E> -// V loadWithMap(Class vClass, Class mClass, Class eClass, int length, -// Class> vectorIndexClass, int indexLength, -// Object base, long offset, -// W indexVector1, W indexVector2, W indexVector3, W indexVector4, -// M m, C container, int index, int[] indexMap, int indexM, S s, -// LoadVectorOperationWithMap defaultImpl) -// +// V loadWithMap(Class vClass, Class mClass, int laneType, +// int length, +// Class> vectorIndexClass, +// int indexLength, Object base, long offset, +// W indexVector1, W indexVector2, W indexVector3, W indexVector4, +// M m, C container, int index, int[] indexMap, int indexM, S s, +// LoadVectorOperationWithMap defaultImpl) // , // W extends Vector, // M extends VectorMask, // E> -// void storeWithMap(Class vClass, Class mClass, Class eClass, int length, -// Class> vectorIndexClass, int indexLength, -// Object base, long offset, // Unsafe addressing -// W indexVector, V v, M m, -// C container, int index, int[] indexMap, int indexM, // Arguments for default implementation -// StoreVectorOperationWithMap defaultImpl) +// void storeWithMap(Class vClass, Class mClass, int laneType, +// int length, +// Class> vectorIndexClass, +// int indexLength, Object base, long offset, +// W indexVector, +// V v, M m, C container, int index, int[] indexMap, int indexM, +// StoreVectorOperationWithMap defaultImpl) +// // bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(1))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(2))->isa_int(); const TypeInt* vlen = gvn().type(argument(3))->isa_int(); const TypeInstPtr* vector_idx_klass = gvn().type(argument(4))->isa_instptr(); const TypeInt* idx_vlen = gvn().type(argument(5))->isa_int(); if (vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con() || vector_idx_klass == nullptr || vector_idx_klass->const_oop() == nullptr || idx_vlen == nullptr || !idx_vlen->is_con()) { @@ -1245,13 +1290,13 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; } - BasicType elem_bt = elem_type->basic_type(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); int num_elem = vlen->get_con(); int idx_num_elem = idx_vlen->get_con(); @@ -1395,23 +1440,26 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { return true; } -// public static -// , -// M extends VectorMask, -// E> -// long reductionCoerced(int oprId, Class vectorClass, Class maskClass, -// Class elementType, int length, V v, M m, -// ReductionOperation defaultImpl) +// +// , +// M extends VectorMask, +// E> +// long reductionCoerced(int oprId, +// Class vClass, Class mClass, int laneType, +// int length, +// V v, M m, +// ReductionOperation defaultImpl) +// bool LibraryCallKit::inline_vector_reduction() { const TypeInt* opr = gvn().type(argument(0))->isa_int(); const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(2))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(3))->isa_int(); const TypeInt* vlen = gvn().type(argument(4))->isa_int(); if (opr == nullptr || !opr->is_con() || vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con()) { log_if_needed(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], @@ -1424,12 +1472,14 @@ bool LibraryCallKit::inline_vector_reduction() { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; } + BasicType elem_bt = get_vector_primitive_lane_type(vltype); const Type* vmask_type = gvn().type(argument(6)); bool is_masked_op = vmask_type != TypePtr::NULL_PTR; if (is_masked_op) { @@ -1449,9 +1499,8 @@ bool LibraryCallKit::inline_vector_reduction() { } } - BasicType elem_bt = elem_type->basic_type(); int num_elem = vlen->get_con(); - int opc = VectorSupport::vop2ideal(opr->get_con(), elem_bt); + int opc = VectorSupport::vop2ideal(opr->get_con(), vltype); int sopc = ReductionNode::opcode(opc, elem_bt); // Ensure reduction operation for lanewise operation @@ -1539,19 +1588,27 @@ bool LibraryCallKit::inline_vector_reduction() { return true; } -// public static boolean test(int cond, Class vectorClass, Class elementType, int vlen, -// V v1, V v2, -// BiFunction defaultImpl) + + +// +// , +// E> +// boolean test(int cond, +// Class mClass, int laneType, +// int length, +// M m1, M m2, +// BiFunction defaultImpl) +// // bool LibraryCallKit::inline_vector_test() { const TypeInt* cond = gvn().type(argument(0))->isa_int(); const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(2))->isa_int(); const TypeInt* vlen = gvn().type(argument(3))->isa_int(); if (cond == nullptr || !cond->is_con() || vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con()) { log_if_needed(" ** missing constant: cond=%s vclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], @@ -1560,17 +1617,20 @@ bool LibraryCallKit::inline_vector_test() { NodeClassNames[argument(3)->Opcode()]); return false; // not enough info for intrinsification } + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); BoolTest::mask booltest = (BoolTest::mask)cond->get_con(); ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); @@ -1605,24 +1665,26 @@ bool LibraryCallKit::inline_vector_test() { return true; } -// public static -// , -// M extends VectorMask, -// E> -// V blend(Class vectorClass, Class maskClass, Class elementType, int vlen, -// V v1, V v2, M m, -// VectorBlendOp defaultImpl) +// +// , +// M extends VectorMask, +// E> +// V blend(Class vClass, Class mClass, int laneType, +// int length, +// V v1, V v2, M m, +// VectorBlendOp defaultImpl) +// bool LibraryCallKit::inline_vector_blend() { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(1))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(2))->isa_int(); const TypeInt* vlen = gvn().type(argument(3))->isa_int(); - if (mask_klass == nullptr || vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr) { + if (mask_klass == nullptr || vector_klass == nullptr || vlen == nullptr || laneType == nullptr) { return false; // dead code } if (mask_klass->const_oop() == nullptr || vector_klass->const_oop() == nullptr || - elem_klass->const_oop() == nullptr || !vlen->is_con()) { + !vlen->is_con() || !laneType->is_con()) { log_if_needed(" ** missing constant: vclass=%s mclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], NodeClassNames[argument(1)->Opcode()], @@ -1630,16 +1692,19 @@ bool LibraryCallKit::inline_vector_blend() { NodeClassNames[argument(3)->Opcode()]); return false; // not enough info for intrinsification } + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; // should be primitive type + } + if (!is_klass_initialized(vector_klass) || !is_klass_initialized(mask_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - BasicType elem_bt = elem_type->basic_type(); + + BasicType elem_bt = get_vector_primitive_lane_type(vltype); BasicType mask_bt = elem_bt; int num_elem = vlen->get_con(); @@ -1670,25 +1735,29 @@ bool LibraryCallKit::inline_vector_blend() { return true; } -// public static + +// // , // M extends VectorMask, // E> -// M compare(int cond, Class vectorClass, Class maskClass, Class elementType, int vlen, +// M compare(int cond, +// Class vectorClass, Class mClass, int laneType, +// int length, // V v1, V v2, M m, -// VectorCompareOp defaultImpl) +// VectorCompareOp defaultImpl) +// bool LibraryCallKit::inline_vector_compare() { const TypeInt* cond = gvn().type(argument(0))->isa_int(); const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(2))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(3))->isa_int(); const TypeInt* vlen = gvn().type(argument(4))->isa_int(); - if (cond == nullptr || vector_klass == nullptr || mask_klass == nullptr || elem_klass == nullptr || vlen == nullptr) { + if (cond == nullptr || vector_klass == nullptr || mask_klass == nullptr || laneType == nullptr || vlen == nullptr) { return false; // dead code } if (!cond->is_con() || vector_klass->const_oop() == nullptr || mask_klass->const_oop() == nullptr || - elem_klass->const_oop() == nullptr || !vlen->is_con()) { + !laneType->is_con() || !vlen->is_con()) { log_if_needed(" ** missing constant: cond=%s vclass=%s mclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], NodeClassNames[argument(1)->Opcode()], @@ -1697,18 +1766,20 @@ bool LibraryCallKit::inline_vector_compare() { NodeClassNames[argument(4)->Opcode()]); return false; // not enough info for intrinsification } + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass) || !is_klass_initialized(mask_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } int num_elem = vlen->get_con(); - BasicType elem_bt = elem_type->basic_type(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); BasicType mask_bt = elem_bt; if ((cond->get_con() & BoolTest::unsigned_compare) != 0) { @@ -1777,27 +1848,30 @@ bool LibraryCallKit::inline_vector_compare() { return true; } -// public static -// , -// Sh extends VectorShuffle, -// M extends VectorMask, -// E> -// V rearrangeOp(Class vectorClass, Class shuffleClass, Class maskClass, Class elementType, int vlen, -// V v1, Sh sh, M m, -// VectorRearrangeOp defaultImpl) +// +// , +// SH extends VectorShuffle, +// M extends VectorMask, +// E> +// V rearrangeOp(Class vClass, Class shClass, Class mClass, int laneType, +// int length, +// V v, SH sh, M m, +// VectorRearrangeOp defaultImpl) +// bool LibraryCallKit::inline_vector_rearrange() { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); const TypeInstPtr* shuffle_klass = gvn().type(argument(1))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(2))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(3))->isa_int(); const TypeInt* vlen = gvn().type(argument(4))->isa_int(); - if (vector_klass == nullptr || shuffle_klass == nullptr || elem_klass == nullptr || vlen == nullptr) { + if (vector_klass == nullptr || shuffle_klass == nullptr || laneType == nullptr || vlen == nullptr) { return false; // dead code } + if (shuffle_klass->const_oop() == nullptr || vector_klass->const_oop() == nullptr || - elem_klass->const_oop() == nullptr || + !laneType->is_con() || !vlen->is_con()) { log_if_needed(" ** missing constant: vclass=%s sclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], @@ -1806,18 +1880,20 @@ bool LibraryCallKit::inline_vector_rearrange() { NodeClassNames[argument(4)->Opcode()]); return false; // not enough info for intrinsification } + if (!is_klass_initialized(vector_klass) || !is_klass_initialized(shuffle_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; } - BasicType elem_bt = elem_type->basic_type(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); BasicType shuffle_bt = elem_bt; if (shuffle_bt == T_FLOAT) { shuffle_bt = T_INT; @@ -1917,22 +1993,23 @@ bool LibraryCallKit::inline_vector_rearrange() { return true; } -// public static -// , -// M extends VectorMask, -// E> -// V selectFromOp(Class vClass, Class mClass, Class eClass, -// int length, V v1, V v2, M m, -// VectorSelectFromOp defaultImpl) +// +// , +// M extends VectorMask, +// E> +// V selectFromOp(Class vClass, Class mClass, int laneType, +// int length, V v1, V v2, M m, +// VectorSelectFromOp defaultImpl) +// bool LibraryCallKit::inline_vector_select_from() { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(1))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(2))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(2))->isa_int(); const TypeInt* vlen = gvn().type(argument(3))->isa_int(); - if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || + if (vector_klass == nullptr || laneType == nullptr || vlen == nullptr || vector_klass->const_oop() == nullptr || - elem_klass->const_oop() == nullptr || + !laneType->is_con() || !vlen->is_con()) { log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], @@ -1940,17 +2017,20 @@ bool LibraryCallKit::inline_vector_select_from() { NodeClassNames[argument(3)->Opcode()]); return false; // not enough info for intrinsification } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; } - BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); if (!is_power_of_2(num_elem)) { log_if_needed(" ** vlen not power of two=%d", num_elem); return false; @@ -2074,25 +2154,27 @@ bool LibraryCallKit::inline_vector_select_from() { return true; } -// public static -// , -// M extends VectorMask, -// E> -// V broadcastInt(int opr, Class vectorClass, Class maskClass, -// Class elementType, int length, -// V v, int n, M m, -// VectorBroadcastIntOp defaultImpl) +// +// , +// M extends VectorMask, +// E> +// V broadcastInt(int opr, +// Class vClass, Class mClass, int laneType, +// int length, +// V v, int n, M m, +// VectorBroadcastIntOp defaultImpl) { +// bool LibraryCallKit::inline_vector_broadcast_int() { const TypeInt* opr = gvn().type(argument(0))->isa_int(); const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(2))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(3))->isa_int(); const TypeInt* vlen = gvn().type(argument(4))->isa_int(); - if (opr == nullptr || vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr) { + if (opr == nullptr || vector_klass == nullptr || laneType == nullptr || vlen == nullptr) { return false; // dead code } - if (!opr->is_con() || vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con()) { + if (!opr->is_con() || vector_klass->const_oop() == nullptr || !laneType->is_con() || !vlen->is_con()) { log_if_needed(" ** missing constant: opr=%s vclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], NodeClassNames[argument(1)->Opcode()], @@ -2105,6 +2187,12 @@ bool LibraryCallKit::inline_vector_broadcast_int() { return false; } + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + const Type* vmask_type = gvn().type(argument(7)); bool is_masked_op = vmask_type != TypePtr::NULL_PTR; if (is_masked_op) { @@ -2124,15 +2212,9 @@ bool LibraryCallKit::inline_vector_broadcast_int() { } } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - int num_elem = vlen->get_con(); - BasicType elem_bt = elem_type->basic_type(); - int opc = VectorSupport::vop2ideal(opr->get_con(), elem_bt); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); + int opc = VectorSupport::vop2ideal(opr->get_con(), vltype); bool is_shift = VectorNode::is_shift_opcode(opc); bool is_rotate = VectorNode::is_rotate_opcode(opc); @@ -2222,12 +2304,12 @@ bool LibraryCallKit::inline_vector_broadcast_int() { return true; } -// public static +// // VOUT convert(int oprId, -// Class fromVectorClass, Class fromElementType, int fromVLen, -// Class toVectorClass, Class toElementType, int toVLen, +// Class fromVectorClass, int fromLaneType, int fromVLen, +// Class toVectorClass, int toLaneType, int toVLen, // VIN v, S s, // VectorConvertOp defaultImpl) // @@ -2235,22 +2317,22 @@ bool LibraryCallKit::inline_vector_convert() { const TypeInt* opr = gvn().type(argument(0))->isa_int(); const TypeInstPtr* vector_klass_from = gvn().type(argument(1))->isa_instptr(); - const TypeInstPtr* elem_klass_from = gvn().type(argument(2))->isa_instptr(); + const TypeInt* laneType_from = gvn().type(argument(2))->isa_int(); const TypeInt* vlen_from = gvn().type(argument(3))->isa_int(); const TypeInstPtr* vector_klass_to = gvn().type(argument(4))->isa_instptr(); - const TypeInstPtr* elem_klass_to = gvn().type(argument(5))->isa_instptr(); + const TypeInt* laneType_to = gvn().type(argument(5))->isa_int(); const TypeInt* vlen_to = gvn().type(argument(6))->isa_int(); if (opr == nullptr || - vector_klass_from == nullptr || elem_klass_from == nullptr || vlen_from == nullptr || - vector_klass_to == nullptr || elem_klass_to == nullptr || vlen_to == nullptr) { + vector_klass_from == nullptr || laneType_from == nullptr || vlen_from == nullptr || + vector_klass_to == nullptr || laneType_to == nullptr || vlen_to == nullptr) { return false; // dead code } if (!opr->is_con() || - vector_klass_from->const_oop() == nullptr || elem_klass_from->const_oop() == nullptr || !vlen_from->is_con() || - vector_klass_to->const_oop() == nullptr || elem_klass_to->const_oop() == nullptr || !vlen_to->is_con()) { - log_if_needed(" ** missing constant: opr=%s vclass_from=%s etype_from=%s vlen_from=%s vclass_to=%s etype_to=%s vlen_to=%s", + vector_klass_from->const_oop() == nullptr || !laneType_from->is_con() || !vlen_from->is_con() || + vector_klass_to->const_oop() == nullptr || !laneType_to->is_con() || !vlen_to->is_con()) { + log_if_needed(" ** missing constant: opr=%s vclass_from=%s laneType_from=%s vlen_from=%s vclass_to=%s laneType_to=%s vlen_to=%s", NodeClassNames[argument(0)->Opcode()], NodeClassNames[argument(1)->Opcode()], NodeClassNames[argument(2)->Opcode()], @@ -2276,16 +2358,19 @@ bool LibraryCallKit::inline_vector_convert() { bool is_mask = is_vector_mask(vbox_klass_from); - ciType* elem_type_from = elem_klass_from->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type_from->is_primitive_type()) { + VectorSupport::LaneType vltype_from = static_cast(laneType_from->get_con()); + if (!is_primitive_lane_type(vltype_from)) { + log_if_needed(" ** not a primitive from lt=%s", VectorSupport::lanetype2name(vltype_from)); return false; // should be primitive type } - BasicType elem_bt_from = elem_type_from->basic_type(); - ciType* elem_type_to = elem_klass_to->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type_to->is_primitive_type()) { + + VectorSupport::LaneType vltype_to = static_cast(laneType_to->get_con()); + if (!is_primitive_lane_type(vltype_to)) { + log_if_needed(" ** not a primitive to lt=%s", VectorSupport::lanetype2name(vltype_to)); return false; // should be primitive type } - BasicType elem_bt_to = elem_type_to->basic_type(); + BasicType elem_bt_from = get_vector_primitive_lane_type(vltype_from); + BasicType elem_bt_to = get_vector_primitive_lane_type(vltype_to); int num_elem_from = vlen_from->get_con(); int num_elem_to = vlen_to->get_con(); @@ -2432,22 +2517,25 @@ bool LibraryCallKit::inline_vector_convert() { return true; } -// public static +// // , // E> -// V insert(Class vectorClass, Class elementType, int vlen, -// V vec, int ix, long val, +// V insert(Class vClass, int laneType, +// int length, +// V v, int i, long val, // VecInsertOp defaultImpl) +// bool LibraryCallKit::inline_vector_insert() { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(1))->isa_int(); const TypeInt* vlen = gvn().type(argument(2))->isa_int(); const TypeInt* idx = gvn().type(argument(4))->isa_int(); - if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || idx == nullptr) { + if (vector_klass == nullptr || laneType == nullptr || vlen == nullptr || idx == nullptr) { return false; // dead code } - if (vector_klass->const_oop() == nullptr || elem_klass->const_oop() == nullptr || !vlen->is_con() || !idx->is_con()) { + if (vector_klass->const_oop() == nullptr || !laneType->is_con() || + !vlen->is_con() || !idx->is_con()) { log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s idx=%s", NodeClassNames[argument(0)->Opcode()], NodeClassNames[argument(1)->Opcode()], @@ -2455,17 +2543,20 @@ bool LibraryCallKit::inline_vector_insert() { NodeClassNames[argument(4)->Opcode()]); return false; // not enough info for intrinsification } + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - BasicType elem_bt = elem_type->basic_type(); + int num_elem = vlen->get_con(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); if (!arch_supports_vector(Op_VectorInsert, num_elem, elem_bt, VecMaskNotUsed)) { log_if_needed(" ** not supported: arity=1 op=insert vlen=%d etype=%s ismask=no", num_elem, type2name(elem_bt)); @@ -2515,41 +2606,45 @@ bool LibraryCallKit::inline_vector_insert() { return true; } -// public static +// // -// long extract(Class vClass, Class eClass, +// long extract(Class vClass, int laneType, // int length, // VM vm, int i, // VecExtractOp defaultImpl) +// bool LibraryCallKit::inline_vector_extract() { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(1))->isa_int(); const TypeInt* vlen = gvn().type(argument(2))->isa_int(); const TypeInt* idx = gvn().type(argument(4))->isa_int(); if (vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con() || idx == nullptr) { - log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s", + log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s idx=%s", NodeClassNames[argument(0)->Opcode()], NodeClassNames[argument(1)->Opcode()], - NodeClassNames[argument(2)->Opcode()]); + NodeClassNames[argument(2)->Opcode()], + NodeClassNames[argument(3)->Opcode()]); return false; // not enough info for intrinsification } + + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - BasicType elem_bt = elem_type->basic_type(); - int num_elem = vlen->get_con(); + int num_elem = vlen->get_con(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); ciKlass* vbox_klass = vector_klass->const_oop()->as_instance()->java_lang_Class_klass(); const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); @@ -2699,19 +2794,21 @@ static Node* LowerSelectFromTwoVectorOperation(PhaseGVN& phase, Node* index_vec, return new VectorBlendNode(p2, p1, mask); } -// public static +// // , // E> -// V selectFromTwoVectorOp(Class vClass, Class eClass, int length, +// V selectFromTwoVectorOp(Class vClass, int laneType, int length, // V v1, V v2, V v3, // SelectFromTwoVector defaultImpl) +// bool LibraryCallKit::inline_vector_select_from_two_vectors() { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(1))->isa_int(); const TypeInt* vlen = gvn().type(argument(2))->isa_int(); - if (vector_klass == nullptr || elem_klass == nullptr || vlen == nullptr || vector_klass->const_oop() == nullptr || - elem_klass->const_oop() == nullptr ||!vlen->is_con()) { + if (vector_klass == nullptr || laneType == nullptr || vlen == nullptr || + vector_klass->const_oop() == nullptr || + !laneType->is_con() || !vlen->is_con()) { log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], NodeClassNames[argument(1)->Opcode()], @@ -2719,24 +2816,24 @@ bool LibraryCallKit::inline_vector_select_from_two_vectors() { return false; // not enough info for intrinsification } + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - int num_elem = vlen->get_con(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); if (!is_power_of_2(num_elem)) { log_if_needed(" ** vlen is not power of two=%d", num_elem); return false; } - BasicType elem_bt = elem_type->basic_type(); BasicType index_elem_bt = elem_bt; if (elem_bt == T_FLOAT) { index_elem_bt = T_INT; @@ -2822,25 +2919,26 @@ bool LibraryCallKit::inline_vector_select_from_two_vectors() { return true; } -// public static -// , -// M extends VectorMask, -// E> -// V compressExpandOp(int opr, -// Class vClass, Class mClass, Class eClass, -// int length, V v, M m, -// CompressExpandOperation defaultImpl) +// +// , +// M extends VectorMask, +// E> +// VectorPayload compressExpandOp(int opr, +// Class vClass, Class mClass, int laneType, +// int length, V v, M m, +// CompressExpandOperation defaultImpl) +// bool LibraryCallKit::inline_vector_compress_expand() { const TypeInt* opr = gvn().type(argument(0))->isa_int(); const TypeInstPtr* vector_klass = gvn().type(argument(1))->isa_instptr(); const TypeInstPtr* mask_klass = gvn().type(argument(2))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(3))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(3))->isa_int(); const TypeInt* vlen = gvn().type(argument(4))->isa_int(); if (opr == nullptr || !opr->is_con() || vector_klass == nullptr || vector_klass->const_oop() == nullptr || mask_klass == nullptr || mask_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con()) { log_if_needed(" ** missing constant: opr=%s vclass=%s mclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], @@ -2856,15 +2954,15 @@ bool LibraryCallKit::inline_vector_compress_expand() { return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; } int num_elem = vlen->get_con(); - BasicType elem_bt = elem_type->basic_type(); - int opc = VectorSupport::vop2ideal(opr->get_con(), elem_bt); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); + int opc = VectorSupport::vop2ideal(opr->get_con(), vltype); if (!arch_supports_vector(opc, num_elem, elem_bt, VecMaskUseLoad)) { log_if_needed(" ** not supported: opc=%d vlen=%d etype=%s ismask=useload", @@ -2907,21 +3005,22 @@ bool LibraryCallKit::inline_vector_compress_expand() { return true; } -// public static -// , -// E, -// S extends VectorSpecies> -// V indexVector(Class vClass, Class eClass, +// +// , +// E, +// S extends VectorSpecies> +// V indexVector(Class vClass, int laneType, // int length, // V v, int step, S s, -// IndexOperation defaultImpl) +// IndexOperation defaultImpl) { +// bool LibraryCallKit::inline_index_vector() { const TypeInstPtr* vector_klass = gvn().type(argument(0))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(1))->isa_int(); const TypeInt* vlen = gvn().type(argument(2))->isa_int(); if (vector_klass == nullptr || vector_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con() ) { log_if_needed(" ** missing constant: vclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], @@ -2930,19 +3029,19 @@ bool LibraryCallKit::inline_index_vector() { return false; // not enough info for intrinsification } + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(vector_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - int num_elem = vlen->get_con(); - BasicType elem_bt = elem_type->basic_type(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); // Check whether the iota index generation op is supported by the current hardware if (!arch_supports_vector(Op_VectorLoadConst, num_elem, elem_bt, VecMaskNotUsed)) { @@ -2950,7 +3049,7 @@ bool LibraryCallKit::inline_index_vector() { return false; // not supported } - int mul_op = VectorSupport::vop2ideal(VectorSupport::VECTOR_OP_MUL, elem_bt); + int mul_op = VectorSupport::vop2ideal(VectorSupport::VECTOR_OP_MUL, vltype); int vmul_op = VectorNode::opcode(mul_op, elem_bt); bool needs_mul = true; Node* scale = argument(4); @@ -2986,7 +3085,7 @@ bool LibraryCallKit::inline_index_vector() { return false; } - int add_op = VectorSupport::vop2ideal(VectorSupport::VECTOR_OP_ADD, elem_bt); + int add_op = VectorSupport::vop2ideal(VectorSupport::VECTOR_OP_ADD, vltype); int vadd_op = VectorNode::opcode(add_op, elem_bt); bool needs_add = true; // The addition is not needed if all the element values of "opd" are zero @@ -3043,19 +3142,20 @@ bool LibraryCallKit::inline_index_vector() { return true; } -// public static -// > -// M indexPartiallyInUpperRange(Class mClass, Class eClass, int length, -// long offset, long limit, -// IndexPartiallyInUpperRangeOperation defaultImpl) +// +// > +// M indexPartiallyInUpperRange(Class mClass, int laneType, +// int length, long offset, long limit, +// IndexPartiallyInUpperRangeOperation defaultImpl) +// bool LibraryCallKit::inline_index_partially_in_upper_range() { const TypeInstPtr* mask_klass = gvn().type(argument(0))->isa_instptr(); - const TypeInstPtr* elem_klass = gvn().type(argument(1))->isa_instptr(); + const TypeInt* laneType = gvn().type(argument(1))->isa_int(); const TypeInt* vlen = gvn().type(argument(2))->isa_int(); if (mask_klass == nullptr || mask_klass->const_oop() == nullptr || - elem_klass == nullptr || elem_klass->const_oop() == nullptr || + laneType == nullptr || !laneType->is_con() || vlen == nullptr || !vlen->is_con()) { log_if_needed(" ** missing constant: mclass=%s etype=%s vlen=%s", NodeClassNames[argument(0)->Opcode()], @@ -3064,19 +3164,19 @@ bool LibraryCallKit::inline_index_partially_in_upper_range() { return false; // not enough info for intrinsification } + VectorSupport::LaneType vltype = static_cast(laneType->get_con()); + if (!is_primitive_lane_type(vltype)) { + log_if_needed(" ** not a primitive lt=%s", VectorSupport::lanetype2name(vltype)); + return false; + } + if (!is_klass_initialized(mask_klass)) { log_if_needed(" ** klass argument not initialized"); return false; } - ciType* elem_type = elem_klass->const_oop()->as_instance()->java_mirror_type(); - if (!elem_type->is_primitive_type()) { - log_if_needed(" ** not a primitive bt=%d", elem_type->basic_type()); - return false; // should be primitive type - } - int num_elem = vlen->get_con(); - BasicType elem_bt = elem_type->basic_type(); + BasicType elem_bt = get_vector_primitive_lane_type(vltype); // Check whether the necessary ops are supported by current hardware. bool supports_mask_gen = arch_supports_vector(Op_VectorMaskGen, num_elem, elem_bt, VecMaskUseStore); diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index a98cad07227..9c0ec113a02 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.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 @@ -199,395 +199,408 @@ bool VectorSupport::is_unsigned_op(jint id) { } } -int VectorSupport::vop2ideal(jint id, BasicType bt) { +const char* VectorSupport::lanetype2name(LaneType lane_type) { + assert(lane_type >= LT_FLOAT && lane_type <= LT_LONG, ""); + const char* lanetype2name[] = { + "float", + "double", + "byte", + "short", + "int", + "long" + }; + return lanetype2name[lane_type]; +} + +int VectorSupport::vop2ideal(jint id, LaneType lt) { VectorOperation vop = (VectorOperation)id; switch (vop) { case VECTOR_OP_ADD: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_AddI; - case T_LONG: return Op_AddL; - case T_FLOAT: return Op_AddF; - case T_DOUBLE: return Op_AddD; - default: fatal("ADD: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_AddI; + case LT_LONG: return Op_AddL; + case LT_FLOAT: return Op_AddF; + case LT_DOUBLE: return Op_AddD; + default: fatal("ADD: %s", lanetype2name(lt)); } break; } case VECTOR_OP_SUB: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_SubI; - case T_LONG: return Op_SubL; - case T_FLOAT: return Op_SubF; - case T_DOUBLE: return Op_SubD; - default: fatal("SUB: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_SubI; + case LT_LONG: return Op_SubL; + case LT_FLOAT: return Op_SubF; + case LT_DOUBLE: return Op_SubD; + default: fatal("SUB: %s", lanetype2name(lt)); } break; } case VECTOR_OP_MUL: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_MulI; - case T_LONG: return Op_MulL; - case T_FLOAT: return Op_MulF; - case T_DOUBLE: return Op_MulD; - default: fatal("MUL: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_MulI; + case LT_LONG: return Op_MulL; + case LT_FLOAT: return Op_MulF; + case LT_DOUBLE: return Op_MulD; + default: fatal("MUL: %s", lanetype2name(lt)); } break; } case VECTOR_OP_DIV: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_DivI; - case T_LONG: return Op_DivL; - case T_FLOAT: return Op_DivF; - case T_DOUBLE: return Op_DivD; - default: fatal("DIV: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_DivI; + case LT_LONG: return Op_DivL; + case LT_FLOAT: return Op_DivF; + case LT_DOUBLE: return Op_DivD; + default: fatal("DIV: %s", lanetype2name(lt)); } break; } case VECTOR_OP_MIN: { - switch (bt) { - case T_BYTE: - case T_SHORT: - case T_INT: return Op_MinI; - case T_LONG: return Op_MinL; - case T_FLOAT: return Op_MinF; - case T_DOUBLE: return Op_MinD; - default: fatal("MIN: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: + case LT_SHORT: + case LT_INT: return Op_MinI; + case LT_LONG: return Op_MinL; + case LT_FLOAT: return Op_MinF; + case LT_DOUBLE: return Op_MinD; + default: fatal("MIN: %s", lanetype2name(lt)); } break; } case VECTOR_OP_MAX: { - switch (bt) { - case T_BYTE: - case T_SHORT: - case T_INT: return Op_MaxI; - case T_LONG: return Op_MaxL; - case T_FLOAT: return Op_MaxF; - case T_DOUBLE: return Op_MaxD; - default: fatal("MAX: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: + case LT_SHORT: + case LT_INT: return Op_MaxI; + case LT_LONG: return Op_MaxL; + case LT_FLOAT: return Op_MaxF; + case LT_DOUBLE: return Op_MaxD; + default: fatal("MAX: %s", lanetype2name(lt)); } break; } case VECTOR_OP_UMIN: { - switch (bt) { - case T_BYTE: - case T_SHORT: - case T_INT: - case T_LONG: return Op_UMinV; - default: fatal("MIN: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: + case LT_SHORT: + case LT_INT: + case LT_LONG: return Op_UMinV; + default: fatal("MIN: %s", lanetype2name(lt)); } break; } case VECTOR_OP_UMAX: { - switch (bt) { - case T_BYTE: - case T_SHORT: - case T_INT: - case T_LONG: return Op_UMaxV; - default: fatal("MAX: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: + case LT_SHORT: + case LT_INT: + case LT_LONG: return Op_UMaxV; + default: fatal("MAX: %s", lanetype2name(lt)); } break; } case VECTOR_OP_ABS: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_AbsI; - case T_LONG: return Op_AbsL; - case T_FLOAT: return Op_AbsF; - case T_DOUBLE: return Op_AbsD; - default: fatal("ABS: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_AbsI; + case LT_LONG: return Op_AbsL; + case LT_FLOAT: return Op_AbsF; + case LT_DOUBLE: return Op_AbsD; + default: fatal("ABS: %s", lanetype2name(lt)); } break; } case VECTOR_OP_NEG: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_NegI; - case T_LONG: return Op_NegL; - case T_FLOAT: return Op_NegF; - case T_DOUBLE: return Op_NegD; - default: fatal("NEG: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_NegI; + case LT_LONG: return Op_NegL; + case LT_FLOAT: return Op_NegF; + case LT_DOUBLE: return Op_NegD; + default: fatal("NEG: %s", lanetype2name(lt)); } break; } case VECTOR_OP_AND: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_AndI; - case T_LONG: return Op_AndL; - default: fatal("AND: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_AndI; + case LT_LONG: return Op_AndL; + default: fatal("AND: %s", lanetype2name(lt)); } break; } case VECTOR_OP_OR: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_OrI; - case T_LONG: return Op_OrL; - default: fatal("OR: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_OrI; + case LT_LONG: return Op_OrL; + default: fatal("OR: %s", lanetype2name(lt)); } break; } case VECTOR_OP_XOR: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_XorI; - case T_LONG: return Op_XorL; - default: fatal("XOR: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_XorI; + case LT_LONG: return Op_XorL; + default: fatal("XOR: %s", lanetype2name(lt)); } break; } case VECTOR_OP_SQRT: { - switch (bt) { - case T_FLOAT: return Op_SqrtF; - case T_DOUBLE: return Op_SqrtD; - default: fatal("SQRT: %s", type2name(bt)); + switch (lt) { + case LT_FLOAT: return Op_SqrtF; + case LT_DOUBLE: return Op_SqrtD; + default: fatal("SQRT: %s", lanetype2name(lt)); } break; } case VECTOR_OP_FMA: { - switch (bt) { - case T_FLOAT: return Op_FmaF; - case T_DOUBLE: return Op_FmaD; - default: fatal("FMA: %s", type2name(bt)); + switch (lt) { + case LT_FLOAT: return Op_FmaF; + case LT_DOUBLE: return Op_FmaD; + default: fatal("FMA: %s", lanetype2name(lt)); } break; } case VECTOR_OP_LSHIFT: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_LShiftI; - case T_LONG: return Op_LShiftL; - default: fatal("LSHIFT: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_LShiftI; + case LT_LONG: return Op_LShiftL; + default: fatal("LSHIFT: %s", lanetype2name(lt)); } break; } case VECTOR_OP_RSHIFT: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: return Op_RShiftI; - case T_LONG: return Op_RShiftL; - default: fatal("RSHIFT: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: return Op_RShiftI; + case LT_LONG: return Op_RShiftL; + default: fatal("RSHIFT: %s", lanetype2name(lt)); } break; } case VECTOR_OP_URSHIFT: { - switch (bt) { - case T_BYTE: return Op_URShiftB; - case T_SHORT: return Op_URShiftS; - case T_INT: return Op_URShiftI; - case T_LONG: return Op_URShiftL; - default: fatal("URSHIFT: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: return Op_URShiftB; + case LT_SHORT: return Op_URShiftS; + case LT_INT: return Op_URShiftI; + case LT_LONG: return Op_URShiftL; + default: fatal("URSHIFT: %s", lanetype2name(lt)); } break; } case VECTOR_OP_LROTATE: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: return Op_RotateLeft; - default: fatal("LROTATE: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: return Op_RotateLeft; + default: fatal("LROTATE: %s", lanetype2name(lt)); } break; } case VECTOR_OP_RROTATE: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: return Op_RotateRight; - default: fatal("RROTATE: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: return Op_RotateRight; + default: fatal("RROTATE: %s", lanetype2name(lt)); } break; } case VECTOR_OP_MASK_LASTTRUE: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: // fall-through - case T_FLOAT: // fall-through - case T_DOUBLE: return Op_VectorMaskLastTrue; - default: fatal("MASK_LASTTRUE: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: // fall-through + case LT_FLOAT: // fall-through + case LT_DOUBLE: return Op_VectorMaskLastTrue; + default: fatal("MASK_LASTTRUE: %s", lanetype2name(lt)); } break; } case VECTOR_OP_MASK_FIRSTTRUE: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: // fall-through - case T_FLOAT: // fall-through - case T_DOUBLE: return Op_VectorMaskFirstTrue; - default: fatal("MASK_FIRSTTRUE: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: // fall-through + case LT_FLOAT: // fall-through + case LT_DOUBLE: return Op_VectorMaskFirstTrue; + default: fatal("MASK_FIRSTTRUE: %s", lanetype2name(lt)); } break; } case VECTOR_OP_MASK_TRUECOUNT: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: // fall-through - case T_FLOAT: // fall-through - case T_DOUBLE: return Op_VectorMaskTrueCount; - default: fatal("MASK_TRUECOUNT: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: // fall-through + case LT_FLOAT: // fall-through + case LT_DOUBLE: return Op_VectorMaskTrueCount; + default: fatal("MASK_TRUECOUNT: %s", lanetype2name(lt)); } break; } case VECTOR_OP_MASK_TOLONG: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: // fall-through - case T_FLOAT: // fall-through - case T_DOUBLE: return Op_VectorMaskToLong; - default: fatal("MASK_TOLONG: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: // fall-through + case LT_FLOAT: // fall-through + case LT_DOUBLE: return Op_VectorMaskToLong; + default: fatal("MASK_TOLONG: %s", lanetype2name(lt)); } break; } case VECTOR_OP_EXPAND: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: // fall-through - case T_FLOAT: // fall-through - case T_DOUBLE: return Op_ExpandV; - default: fatal("EXPAND: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: // fall-through + case LT_FLOAT: // fall-through + case LT_DOUBLE: return Op_ExpandV; + default: fatal("EXPAND: %s", lanetype2name(lt)); } break; } case VECTOR_OP_COMPRESS: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: // fall-through - case T_FLOAT: // fall-through - case T_DOUBLE: return Op_CompressV; - default: fatal("COMPRESS: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: // fall-through + case LT_FLOAT: // fall-through + case LT_DOUBLE: return Op_CompressV; + default: fatal("COMPRESS: %s", lanetype2name(lt)); } break; } case VECTOR_OP_MASK_COMPRESS: { - switch (bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: // fall-through - case T_FLOAT: // fall-through - case T_DOUBLE: return Op_CompressM; - default: fatal("MASK_COMPRESS: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: // fall-through + case LT_FLOAT: // fall-through + case LT_DOUBLE: return Op_CompressM; + default: fatal("MASK_COMPRESS: %s", lanetype2name(lt)); } break; } case VECTOR_OP_BIT_COUNT: { - switch (bt) { - case T_BYTE: // Returning Op_PopCountI - case T_SHORT: // for byte and short types temporarily - case T_INT: return Op_PopCountI; - case T_LONG: return Op_PopCountL; - default: fatal("BIT_COUNT: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // Returning Op_PopCountI + case LT_SHORT: // for byte and short types temporarily + case LT_INT: return Op_PopCountI; + case LT_LONG: return Op_PopCountL; + default: fatal("BILT_COUNT: %s", lanetype2name(lt)); } break; } case VECTOR_OP_TZ_COUNT: { - switch (bt) { - case T_BYTE: - case T_SHORT: - case T_INT: return Op_CountTrailingZerosI; - case T_LONG: return Op_CountTrailingZerosL; - default: fatal("TZ_COUNT: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: + case LT_SHORT: + case LT_INT: return Op_CountTrailingZerosI; + case LT_LONG: return Op_CountTrailingZerosL; + default: fatal("TZ_COUNT: %s", lanetype2name(lt)); } break; } case VECTOR_OP_LZ_COUNT: { - switch (bt) { - case T_BYTE: - case T_SHORT: - case T_INT: return Op_CountLeadingZerosI; - case T_LONG: return Op_CountLeadingZerosL; - default: fatal("LZ_COUNT: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: + case LT_SHORT: + case LT_INT: return Op_CountLeadingZerosI; + case LT_LONG: return Op_CountLeadingZerosL; + default: fatal("LZ_COUNT: %s", lanetype2name(lt)); } break; } case VECTOR_OP_REVERSE: { - switch (bt) { - case T_BYTE: // Temporarily returning - case T_SHORT: // Op_ReverseI for byte and short - case T_INT: return Op_ReverseI; - case T_LONG: return Op_ReverseL; - default: fatal("REVERSE: %s", type2name(bt)); + switch (lt) { + case LT_BYTE: // Temporarily returning + case LT_SHORT: // Op_ReverseI for byte and short + case LT_INT: return Op_ReverseI; + case LT_LONG: return Op_ReverseL; + default: fatal("REVERSE: %s", lanetype2name(lt)); } break; } case VECTOR_OP_REVERSE_BYTES: { - switch (bt) { - case T_SHORT: return Op_ReverseBytesS; + switch (lt) { + case LT_SHORT: return Op_ReverseBytesS; // Superword requires type consistency between the ReverseBytes* // node and the data. But there's no ReverseBytesB node because - // no reverseBytes() method in Java Byte class. T_BYTE can only + // no reverseBytes() method in Java Byte class. LT_BYTE can only // appear in VectorAPI calls. We reuse Op_ReverseBytesI for this // to ensure vector intrinsification succeeds. - case T_BYTE: // Intentionally fall-through - case T_INT: return Op_ReverseBytesI; - case T_LONG: return Op_ReverseBytesL; - default: fatal("REVERSE_BYTES: %s", type2name(bt)); + case LT_BYTE: // Intentionally fall-through + case LT_INT: return Op_ReverseBytesI; + case LT_LONG: return Op_ReverseBytesL; + default: fatal("REVERSE_BYTES: %s", lanetype2name(lt)); } break; } case VECTOR_OP_SADD: case VECTOR_OP_SUADD: { - switch(bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: return Op_SaturatingAddV; - default: fatal("S[U]ADD: %s", type2name(bt)); + switch(lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: return Op_SaturatingAddV; + default: fatal("S[U]ADD: %s", lanetype2name(lt)); } break; } case VECTOR_OP_SSUB: case VECTOR_OP_SUSUB: { - switch(bt) { - case T_BYTE: // fall-through - case T_SHORT: // fall-through - case T_INT: // fall-through - case T_LONG: return Op_SaturatingSubV; - default: fatal("S[U}SUB: %s", type2name(bt)); + switch(lt) { + case LT_BYTE: // fall-through + case LT_SHORT: // fall-through + case LT_INT: // fall-through + case LT_LONG: return Op_SaturatingSubV; + default: fatal("S[U}SUB: %s", lanetype2name(lt)); } break; } case VECTOR_OP_COMPRESS_BITS: { - switch (bt) { - case T_INT: - case T_LONG: return Op_CompressBits; - default: fatal("COMPRESS_BITS: %s", type2name(bt)); + switch (lt) { + case LT_INT: + case LT_LONG: return Op_CompressBits; + default: fatal("COMPRESS_BITS: %s", lanetype2name(lt)); } break; } case VECTOR_OP_EXPAND_BITS: { - switch (bt) { - case T_INT: - case T_LONG: return Op_ExpandBits; - default: fatal("EXPAND_BITS: %s", type2name(bt)); + switch (lt) { + case LT_INT: + case LT_LONG: return Op_ExpandBits; + default: fatal("EXPAND_BITS: %s", lanetype2name(lt)); } break; } diff --git a/src/hotspot/share/prims/vectorSupport.hpp b/src/hotspot/share/prims/vectorSupport.hpp index 5dd06f31b16..da3736b4711 100644 --- a/src/hotspot/share/prims/vectorSupport.hpp +++ b/src/hotspot/share/prims/vectorSupport.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 @@ -137,12 +137,23 @@ class VectorSupport : AllStatic { NUM_VEC_SIZES = 5 }; + // Values in this enum correspond to the jdk.incubator.vector.LaneType ordinal values. + enum LaneType : int { + LT_FLOAT = 0, + LT_DOUBLE = 1, + LT_BYTE = 2, + LT_SHORT = 3, + LT_INT = 4, + LT_LONG = 5 + }; + enum { MODE_BROADCAST = 0, MODE_BITS_COERCED_LONG_TO_MASK = 1 }; - static int vop2ideal(jint vop, BasicType bt); + static int vop2ideal(jint vop, LaneType lt); + static const char* lanetype2name(LaneType lane_type); static bool has_scalar_op(jint id); static bool is_unsigned_op(jint id); diff --git a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template index b594aca7a05..a04fea67b88 100644 --- a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template +++ b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template @@ -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 @@ -326,7 +326,7 @@ public final class ScopedMemoryAccess { @ForceInline public static , E, S extends VectorSupport.VectorSpecies> - V loadFromMemorySegment(Class vmClass, Class e, int length, + V loadFromMemorySegment(Class vmClass, int laneType, int length, AbstractMemorySegmentImpl msp, long offset, S s, VectorSupport.LoadOperation defaultImpl) { @@ -334,7 +334,7 @@ public final class ScopedMemoryAccess { try { return loadFromMemorySegmentScopedInternal( msp.sessionImpl(), - vmClass, e, length, + vmClass, laneType, length, msp, offset, s, defaultImpl); @@ -348,14 +348,14 @@ public final class ScopedMemoryAccess { private static , E, S extends VectorSupport.VectorSpecies> V loadFromMemorySegmentScopedInternal(MemorySessionImpl session, - Class vmClass, Class e, int length, + Class vmClass, int laneType, int length, AbstractMemorySegmentImpl msp, long offset, S s, VectorSupport.LoadOperation defaultImpl) { try { session.checkValidStateRaw(); - return VectorSupport.load(vmClass, e, length, + return VectorSupport.load(vmClass, laneType, length, msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, true, msp, offset, s, defaultImpl); @@ -368,14 +368,14 @@ public final class ScopedMemoryAccess { public static , E, S extends VectorSupport.VectorSpecies, M extends VectorSupport.VectorMask> - V loadFromMemorySegmentMasked(Class vmClass, Class maskClass, Class e, + V loadFromMemorySegmentMasked(Class vmClass, Class maskClass, int laneType, int length, AbstractMemorySegmentImpl msp, long offset, M m, S s, int offsetInRange, VectorSupport.LoadVectorMaskedOperation defaultImpl) { try { return loadFromMemorySegmentMaskedScopedInternal( msp.sessionImpl(), - vmClass, maskClass, e, length, + vmClass, maskClass, laneType, length, msp, offset, m, s, offsetInRange, defaultImpl); @@ -390,14 +390,14 @@ public final class ScopedMemoryAccess { , E, S extends VectorSupport.VectorSpecies, M extends VectorSupport.VectorMask> V loadFromMemorySegmentMaskedScopedInternal(MemorySessionImpl session, Class vmClass, - Class maskClass, Class e, int length, + Class maskClass, int laneType, int length, AbstractMemorySegmentImpl msp, long offset, M m, S s, int offsetInRange, VectorSupport.LoadVectorMaskedOperation defaultImpl) { try { session.checkValidStateRaw(); - return VectorSupport.loadMasked(vmClass, maskClass, e, length, + return VectorSupport.loadMasked(vmClass, maskClass, laneType, length, msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, true, m, offsetInRange, msp, offset, s, defaultImpl); @@ -409,7 +409,7 @@ public final class ScopedMemoryAccess { @ForceInline public static , E> - void storeIntoMemorySegment(Class vmClass, Class e, int length, + void storeIntoMemorySegment(Class vmClass, int laneType, int length, V v, AbstractMemorySegmentImpl msp, long offset, VectorSupport.StoreVectorOperation defaultImpl) { @@ -417,7 +417,7 @@ public final class ScopedMemoryAccess { try { storeIntoMemorySegmentScopedInternal( msp.sessionImpl(), - vmClass, e, length, + vmClass, laneType, length, v, msp, offset, defaultImpl); @@ -431,14 +431,14 @@ public final class ScopedMemoryAccess { private static , E> void storeIntoMemorySegmentScopedInternal(MemorySessionImpl session, - Class vmClass, Class e, int length, + Class vmClass, int laneType, int length, V v, AbstractMemorySegmentImpl msp, long offset, VectorSupport.StoreVectorOperation defaultImpl) { try { session.checkValidStateRaw(); - VectorSupport.store(vmClass, e, length, + VectorSupport.store(vmClass, laneType, length, msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, true, v, msp, offset, @@ -451,15 +451,15 @@ public final class ScopedMemoryAccess { @ForceInline public static , E, M extends VectorSupport.VectorMask> - void storeIntoMemorySegmentMasked(Class vmClass, Class maskClass, Class e, - int length, V v, M m, + void storeIntoMemorySegmentMasked(Class vmClass, Class maskClass, + int laneType, int length, V v, M m, AbstractMemorySegmentImpl msp, long offset, VectorSupport.StoreVectorMaskedOperation defaultImpl) { try { storeIntoMemorySegmentMaskedScopedInternal( msp.sessionImpl(), - vmClass, maskClass, e, length, + vmClass, maskClass, laneType, length, v, m, msp, offset, defaultImpl); @@ -474,13 +474,13 @@ public final class ScopedMemoryAccess { , E, M extends VectorSupport.VectorMask> void storeIntoMemorySegmentMaskedScopedInternal(MemorySessionImpl session, Class vmClass, Class maskClass, - Class e, int length, V v, M m, + int laneType, int length, V v, M m, AbstractMemorySegmentImpl msp, long offset, VectorSupport.StoreVectorMaskedOperation defaultImpl) { try { session.checkValidStateRaw(); - VectorSupport.storeMasked(vmClass, maskClass, e, length, + VectorSupport.storeMasked(vmClass, maskClass, laneType, length, msp.unsafeGetBase(), msp.unsafeGetOffset() + offset, true, v, m, msp, offset, diff --git a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java index d3705a279ca..03f95222a52 100644 --- a/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.java +++ b/src/java.base/share/classes/jdk/internal/vm/vector/VectorSupport.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 @@ -149,14 +149,14 @@ public class VectorSupport { public static final int MODE_BROADCAST = 0; public static final int MODE_BITS_COERCED_LONG_TO_MASK = 1; - // BasicType codes, for primitives only: + // Values correspond to jdk.incubator.vector.LaneType ordinals public static final int - T_FLOAT = 6, - T_DOUBLE = 7, - T_BYTE = 8, - T_SHORT = 9, - T_INT = 10, - T_LONG = 11; + LT_FLOAT = 0, + LT_DOUBLE = 1, + LT_BYTE = 2, + LT_SHORT = 3, + LT_INT = 4, + LT_LONG = 5; /* ============================================================================ */ @@ -203,7 +203,7 @@ public class VectorSupport { , E> - VM fromBitsCoerced(Class vmClass, Class eClass, + VM fromBitsCoerced(Class vmClass, int laneType, int length, long bits, int mode, S s, FromBitsCoercedOperation defaultImpl) { @@ -221,7 +221,7 @@ public class VectorSupport { public static > - M indexPartiallyInUpperRange(Class mClass, Class eClass, + M indexPartiallyInUpperRange(Class mClass, int laneType, int length, long offset, long limit, IndexPartiallyInUpperRangeOperation defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; @@ -239,7 +239,7 @@ public class VectorSupport { , E, S extends VectorSpecies> - V indexVector(Class vClass, Class eClass, + V indexVector(Class vClass, int laneType, int length, V v, int step, S s, IndexOperation defaultImpl) { @@ -260,7 +260,7 @@ public class VectorSupport { M extends VectorMask, E> long reductionCoerced(int oprId, - Class vClass, Class mClass, Class eClass, + Class vClass, Class mClass, int laneType, int length, V v, M m, ReductionOperation defaultImpl) { @@ -279,7 +279,7 @@ public class VectorSupport { public static - long extract(Class vClass, Class eClass, + long extract(Class vClass, int laneType, int length, VM vm, int i, VecExtractOp defaultImpl) { @@ -297,7 +297,7 @@ public class VectorSupport { public static , E> - V insert(Class vClass, Class eClass, + V insert(Class vClass, int laneType, int length, V v, int i, long val, VecInsertOp defaultImpl) { @@ -318,7 +318,7 @@ public class VectorSupport { M extends VectorMask, E> V unaryOp(int oprId, - Class vClass, Class mClass, Class eClass, + Class vClass, Class mClass, int laneType, int length, V v, M m, UnaryOperation defaultImpl) { @@ -356,7 +356,7 @@ public class VectorSupport { M extends VectorMask, E> VM binaryOp(int oprId, - Class vmClass, Class mClass, Class eClass, + Class vmClass, Class mClass, int laneType, int length, VM v1, VM v2, M m, BinaryOperation defaultImpl) { @@ -391,7 +391,7 @@ public class VectorSupport { public static , E> - V selectFromTwoVectorOp(Class vClass, Class eClass, int length, + V selectFromTwoVectorOp(Class vClass, int laneType, int length, V v1, V v2, V v3, SelectFromTwoVector defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; @@ -413,7 +413,7 @@ public class VectorSupport { M extends VectorMask, E> V ternaryOp(int oprId, - Class vClass, Class mClass, Class eClass, + Class vClass, Class mClass, int laneType, int length, V v1, V v2, V v3, M m, TernaryOperation defaultImpl) { @@ -437,7 +437,7 @@ public class VectorSupport { VM extends VectorPayload, E, S extends VectorSpecies> - VM load(Class vmClass, Class eClass, + VM load(Class vmClass, int laneType, int length, Object base, long offset, boolean fromSegment, C container, long index, S s, @@ -462,7 +462,7 @@ public class VectorSupport { E, S extends VectorSpecies, M extends VectorMask> - V loadMasked(Class vClass, Class mClass, Class eClass, + V loadMasked(Class vClass, Class mClass, int laneType, int length, Object base, long offset, boolean fromSegment, M m, int offsetInRange, C container, long index, S s, @@ -488,7 +488,7 @@ public class VectorSupport { S extends VectorSpecies, M extends VectorMask, E> - V loadWithMap(Class vClass, Class mClass, Class eClass, + V loadWithMap(Class vClass, Class mClass, int laneType, int length, Class> vectorIndexClass, int indexLength, Object base, long offset, @@ -510,7 +510,7 @@ public class VectorSupport { public static - void store(Class vClass, Class eClass, + void store(Class vClass, int laneType, int length, Object base, long offset, boolean fromSegment, V v, C container, long index, @@ -531,7 +531,7 @@ public class VectorSupport { V extends Vector, M extends VectorMask, E> - void storeMasked(Class vClass, Class mClass, Class eClass, + void storeMasked(Class vClass, Class mClass, int laneType, int length, Object base, long offset, boolean fromSegment, V v, M m, C container, long index, @@ -555,7 +555,7 @@ public class VectorSupport { W extends Vector, M extends VectorMask, E> - void storeWithMap(Class vClass, Class mClass, Class eClass, + void storeWithMap(Class vClass, Class mClass, int laneType, int length, Class> vectorIndexClass, int indexLength, Object base, long offset, @@ -573,7 +573,7 @@ public class VectorSupport { , E> boolean test(int cond, - Class mClass, Class eClass, + Class mClass, int laneType, int length, M m1, M m2, BiFunction defaultImpl) { @@ -594,7 +594,7 @@ public class VectorSupport { M extends VectorMask, E> M compare(int cond, - Class vectorClass, Class mClass, Class eClass, + Class vectorClass, Class mClass, int laneType, int length, V v1, V v2, M m, VectorCompareOp defaultImpl) { @@ -615,7 +615,7 @@ public class VectorSupport { SH extends VectorShuffle, M extends VectorMask, E> - V rearrangeOp(Class vClass, Class shClass, Class mClass, Class eClass, + V rearrangeOp(Class vClass, Class shClass, Class mClass, int laneType, int length, V v, SH sh, M m, VectorRearrangeOp defaultImpl) { @@ -633,7 +633,7 @@ public class VectorSupport { , M extends VectorMask, E> - V selectFromOp(Class vClass, Class mClass, Class eClass, + V selectFromOp(Class vClass, Class mClass, int laneType, int length, V v1, V v2, M m, VectorSelectFromOp defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; @@ -652,7 +652,7 @@ public class VectorSupport { , M extends VectorMask, E> - V blend(Class vClass, Class mClass, Class eClass, + V blend(Class vClass, Class mClass, int laneType, int length, V v1, V v2, M m, VectorBlendOp defaultImpl) { @@ -673,7 +673,7 @@ public class VectorSupport { M extends VectorMask, E> V broadcastInt(int opr, - Class vClass, Class mClass, Class eClass, + Class vClass, Class mClass, int laneType, int length, V v, int n, M m, VectorBroadcastIntOp defaultImpl) { @@ -698,8 +698,8 @@ public class VectorSupport { VIN extends VectorPayload, S extends VectorSpecies> VOUT convert(int oprId, - Class fromVectorClass, Class fromeClass, int fromVLen, - Class toVectorClass, Class toeClass, int toVLen, + Class fromVectorClass, int fromLaneType, int fromVLen, + Class toVectorClass, int toLaneType, int toVLen, VIN v, S s, VectorConvertOp defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; @@ -719,7 +719,7 @@ public class VectorSupport { M extends VectorMask, E> VectorPayload compressExpandOp(int opr, - Class vClass, Class mClass, Class eClass, + Class vClass, Class mClass, int laneType, int length, V v, M m, CompressExpandOperation defaultImpl) { assert isNonCapturingLambda(defaultImpl) : defaultImpl; @@ -748,7 +748,7 @@ public class VectorSupport { , E> long maskReductionCoerced(int oper, - Class mClass, Class eClass, + Class mClass, int laneType, int length, M m, VectorMaskOp defaultImpl) { 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 30297b24db0..5b762edfd3b 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, 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 @@ -73,7 +73,7 @@ abstract class AbstractMask extends VectorMask { int laneCount = vsp.laneCount(); i = VectorIntrinsics.checkFromIndexSize(i, laneCount, bits.length); VectorSupport.store( - vsp.maskType(), vsp.elementType(), laneCount, + vsp.maskType(), vsp.laneTypeOrdinal(), laneCount, bits, (long) i + Unsafe.ARRAY_BOOLEAN_BASE_OFFSET, false, this, bits, i, (c, idx, s) -> System.arraycopy(s.getBits(), 0, c, (int) idx, s.length())); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java index 19ee4bb0074..075400a0d4a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractShuffle.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 @@ -53,7 +53,7 @@ abstract class AbstractShuffle extends VectorShuffle { @ForceInline final AbstractVector toBitsVectorTemplate() { AbstractSpecies dsp = vspecies().asIntegral(); - Class etype = dsp.elementType(); + int etype = dsp.laneTypeOrdinal(); Class rvtype = dsp.dummyVector().getClass(); return VectorSupport.convert(VectorSupport.VECTOR_OP_REINTERPRET, getClass(), etype, length(), diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java index bdc72c64ce5..0f9edbe450c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractSpecies.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, 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 @@ -144,6 +144,12 @@ abstract class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.V return (Class) laneType.elementType; } + @ForceInline + @SuppressWarnings("unchecked") + //NOT FINAL: SPECIALIZED + int laneTypeOrdinal() { + return laneType.ordinal(); + } // FIXME: appeal to general method (see https://bugs.openjdk.org/browse/JDK-6176992) // replace usages of this method and remove @ForceInline diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java index 09bb0607759..80260c2bd30 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/AbstractVector.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 @@ -84,6 +84,9 @@ abstract class AbstractVector extends Vector { /*package-private*/ abstract AbstractSpecies vspecies(); + /*package-private*/ + abstract int laneTypeOrdinal(); + @Override @ForceInline public final VectorSpecies species() { @@ -236,9 +239,9 @@ abstract class AbstractVector extends Vector { /*package-private*/ @ForceInline final VectorShuffle bitsToShuffleTemplate(AbstractSpecies dsp) { - Class etype = vspecies().elementType(); + int etype = vspecies().laneTypeOrdinal(); Class dvtype = dsp.shuffleType(); - Class dtype = dsp.asIntegral().elementType(); + int dtype = dsp.asIntegral().laneTypeOrdinal(); int dlength = dsp.dummyVector().length(); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, getClass(), etype, length(), @@ -762,10 +765,10 @@ abstract class AbstractVector extends Vector { AbstractVector convert0(char kind, AbstractSpecies rsp) { // Derive some JIT-time constants: Class vtype; - Class etype; // fill in after switch (constant) + int etype; // fill in after switch (constant) int vlength; // fill in after switch (mark type profile?) Class rvtype; // fill in after switch (mark type profile) - Class rtype; + int rtype; int rlength; switch (kind) { case 'Z': // lane-wise size change, maybe with sign clip @@ -773,9 +776,9 @@ abstract class AbstractVector extends Vector { AbstractSpecies vsp = this.vspecies(); AbstractSpecies vspi = vsp.asIntegral(); AbstractVector biti = vspi == vsp ? this : this.convert0('X', vspi); - rtype = rspi.elementType(); + rtype = rspi.laneTypeOrdinal(); rlength = rspi.laneCount(); - etype = vspi.elementType(); + etype = vspi.laneTypeOrdinal(); vlength = vspi.laneCount(); rvtype = rspi.dummyVector().getClass(); vtype = vspi.dummyVector().getClass(); @@ -787,9 +790,9 @@ abstract class AbstractVector extends Vector { AbstractVector::defaultUCast); return (rspi == rsp ? bitv.check0(rsp) : bitv.convert0('X', rsp)); case 'C': // lane-wise cast (but not identity) - rtype = rsp.elementType(); + rtype = rsp.laneTypeOrdinal(); rlength = rsp.laneCount(); - etype = this.elementType(); // (profile) + etype = this.vspecies().laneTypeOrdinal(); // (profile) vlength = this.length(); // (profile) rvtype = rsp.dummyVector().getClass(); // (profile) vtype = this.getClass(); @@ -799,9 +802,9 @@ abstract class AbstractVector extends Vector { this, rsp, AbstractVector::defaultCast); case 'X': // reinterpret cast, not lane-wise if lane sizes differ - rtype = rsp.elementType(); + rtype = rsp.laneTypeOrdinal(); rlength = rsp.laneCount(); - etype = this.elementType(); // (profile) + etype = this.vspecies().laneTypeOrdinal(); // (profile) vlength = this.length(); // (profile) rvtype = rsp.dummyVector().getClass(); // (profile) vtype = this.getClass(); diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java index 3569ed00f1f..0b710938ede 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte128Vector.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 @@ -119,6 +119,13 @@ final class Byte128Vector extends ByteVector { return (byte[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -541,7 +548,7 @@ final class Byte128Vector extends ByteVector { @ForceInline public byte laneHelper(int i) { return (byte) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { byte[] vecarr = vec.vec(); @@ -576,7 +583,7 @@ final class Byte128Vector extends ByteVector { @ForceInline public Byte128Vector withLaneHelper(int i, byte e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { byte[] res = v.vec().clone(); @@ -680,8 +687,8 @@ final class Byte128Vector extends ByteVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -691,7 +698,7 @@ final class Byte128Vector extends ByteVector { /*package-private*/ Byte128Mask indexPartiallyInUpperRange(long offset, long limit) { return (Byte128Mask) VectorSupport.indexPartiallyInUpperRange( - Byte128Mask.class, byte.class, VLENGTH, offset, limit, + Byte128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Byte128Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -707,7 +714,7 @@ final class Byte128Vector extends ByteVector { @ForceInline public Byte128Mask compress() { return (Byte128Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Byte128Vector.class, Byte128Mask.class, ETYPE, VLENGTH, null, this, + Byte128Vector.class, Byte128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -719,7 +726,7 @@ final class Byte128Vector extends ByteVector { public Byte128Mask and(VectorMask mask) { Objects.requireNonNull(mask); Byte128Mask m = (Byte128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Byte128Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Byte128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -729,7 +736,7 @@ final class Byte128Vector extends ByteVector { public Byte128Mask or(VectorMask mask) { Objects.requireNonNull(mask); Byte128Mask m = (Byte128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Byte128Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Byte128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -739,7 +746,7 @@ final class Byte128Vector extends ByteVector { public Byte128Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Byte128Mask m = (Byte128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Byte128Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Byte128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -749,21 +756,21 @@ final class Byte128Vector extends ByteVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Byte128Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Byte128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Byte128Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Byte128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Byte128Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Byte128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -773,7 +780,7 @@ final class Byte128Vector extends ByteVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Byte128Mask.class, byte.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Byte128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -783,7 +790,7 @@ final class Byte128Vector extends ByteVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Byte128Mask.class, byte.class, VLENGTH, + return VectorSupport.extract(Byte128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -792,7 +799,7 @@ final class Byte128Vector extends ByteVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Byte128Mask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_ne, Byte128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Byte128Mask)m).getBits())); } @@ -800,7 +807,7 @@ final class Byte128Vector extends ByteVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Byte128Mask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_overflow, Byte128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Byte128Mask)m).getBits())); } @@ -808,7 +815,7 @@ final class Byte128Vector extends ByteVector { @ForceInline /*package-private*/ static Byte128Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Byte128Mask.class, byte.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Byte128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java index 70a3306731e..6b0e57f8b8f 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte256Vector.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 @@ -119,6 +119,13 @@ final class Byte256Vector extends ByteVector { return (byte[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -557,7 +564,7 @@ final class Byte256Vector extends ByteVector { @ForceInline public byte laneHelper(int i) { return (byte) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { byte[] vecarr = vec.vec(); @@ -608,7 +615,7 @@ final class Byte256Vector extends ByteVector { @ForceInline public Byte256Vector withLaneHelper(int i, byte e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { byte[] res = v.vec().clone(); @@ -712,8 +719,8 @@ final class Byte256Vector extends ByteVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -723,7 +730,7 @@ final class Byte256Vector extends ByteVector { /*package-private*/ Byte256Mask indexPartiallyInUpperRange(long offset, long limit) { return (Byte256Mask) VectorSupport.indexPartiallyInUpperRange( - Byte256Mask.class, byte.class, VLENGTH, offset, limit, + Byte256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Byte256Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -739,7 +746,7 @@ final class Byte256Vector extends ByteVector { @ForceInline public Byte256Mask compress() { return (Byte256Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Byte256Vector.class, Byte256Mask.class, ETYPE, VLENGTH, null, this, + Byte256Vector.class, Byte256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -751,7 +758,7 @@ final class Byte256Vector extends ByteVector { public Byte256Mask and(VectorMask mask) { Objects.requireNonNull(mask); Byte256Mask m = (Byte256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Byte256Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Byte256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -761,7 +768,7 @@ final class Byte256Vector extends ByteVector { public Byte256Mask or(VectorMask mask) { Objects.requireNonNull(mask); Byte256Mask m = (Byte256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Byte256Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Byte256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -771,7 +778,7 @@ final class Byte256Vector extends ByteVector { public Byte256Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Byte256Mask m = (Byte256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Byte256Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Byte256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -781,21 +788,21 @@ final class Byte256Vector extends ByteVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Byte256Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Byte256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Byte256Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Byte256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Byte256Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Byte256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -805,7 +812,7 @@ final class Byte256Vector extends ByteVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Byte256Mask.class, byte.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Byte256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -815,7 +822,7 @@ final class Byte256Vector extends ByteVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Byte256Mask.class, byte.class, VLENGTH, + return VectorSupport.extract(Byte256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -824,7 +831,7 @@ final class Byte256Vector extends ByteVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Byte256Mask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_ne, Byte256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Byte256Mask)m).getBits())); } @@ -832,7 +839,7 @@ final class Byte256Vector extends ByteVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Byte256Mask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_overflow, Byte256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Byte256Mask)m).getBits())); } @@ -840,7 +847,7 @@ final class Byte256Vector extends ByteVector { @ForceInline /*package-private*/ static Byte256Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Byte256Mask.class, byte.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Byte256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java index a093fe18289..b1df6949e3a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte512Vector.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 @@ -119,6 +119,13 @@ final class Byte512Vector extends ByteVector { return (byte[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -589,7 +596,7 @@ final class Byte512Vector extends ByteVector { @ForceInline public byte laneHelper(int i) { return (byte) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { byte[] vecarr = vec.vec(); @@ -672,7 +679,7 @@ final class Byte512Vector extends ByteVector { @ForceInline public Byte512Vector withLaneHelper(int i, byte e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { byte[] res = v.vec().clone(); @@ -776,8 +783,8 @@ final class Byte512Vector extends ByteVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -787,7 +794,7 @@ final class Byte512Vector extends ByteVector { /*package-private*/ Byte512Mask indexPartiallyInUpperRange(long offset, long limit) { return (Byte512Mask) VectorSupport.indexPartiallyInUpperRange( - Byte512Mask.class, byte.class, VLENGTH, offset, limit, + Byte512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Byte512Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -803,7 +810,7 @@ final class Byte512Vector extends ByteVector { @ForceInline public Byte512Mask compress() { return (Byte512Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Byte512Vector.class, Byte512Mask.class, ETYPE, VLENGTH, null, this, + Byte512Vector.class, Byte512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -815,7 +822,7 @@ final class Byte512Vector extends ByteVector { public Byte512Mask and(VectorMask mask) { Objects.requireNonNull(mask); Byte512Mask m = (Byte512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Byte512Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Byte512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -825,7 +832,7 @@ final class Byte512Vector extends ByteVector { public Byte512Mask or(VectorMask mask) { Objects.requireNonNull(mask); Byte512Mask m = (Byte512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Byte512Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Byte512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -835,7 +842,7 @@ final class Byte512Vector extends ByteVector { public Byte512Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Byte512Mask m = (Byte512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Byte512Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Byte512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -845,21 +852,21 @@ final class Byte512Vector extends ByteVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Byte512Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Byte512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Byte512Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Byte512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Byte512Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Byte512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -869,7 +876,7 @@ final class Byte512Vector extends ByteVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Byte512Mask.class, byte.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Byte512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -879,7 +886,7 @@ final class Byte512Vector extends ByteVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Byte512Mask.class, byte.class, VLENGTH, + return VectorSupport.extract(Byte512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -888,7 +895,7 @@ final class Byte512Vector extends ByteVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Byte512Mask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_ne, Byte512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Byte512Mask)m).getBits())); } @@ -896,7 +903,7 @@ final class Byte512Vector extends ByteVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Byte512Mask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_overflow, Byte512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Byte512Mask)m).getBits())); } @@ -904,7 +911,7 @@ final class Byte512Vector extends ByteVector { @ForceInline /*package-private*/ static Byte512Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Byte512Mask.class, byte.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Byte512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java index cd75ee9f610..dfd16e1812a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Byte64Vector.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 @@ -119,6 +119,13 @@ final class Byte64Vector extends ByteVector { return (byte[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -533,7 +540,7 @@ final class Byte64Vector extends ByteVector { @ForceInline public byte laneHelper(int i) { return (byte) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { byte[] vecarr = vec.vec(); @@ -560,7 +567,7 @@ final class Byte64Vector extends ByteVector { @ForceInline public Byte64Vector withLaneHelper(int i, byte e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { byte[] res = v.vec().clone(); @@ -664,8 +671,8 @@ final class Byte64Vector extends ByteVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -675,7 +682,7 @@ final class Byte64Vector extends ByteVector { /*package-private*/ Byte64Mask indexPartiallyInUpperRange(long offset, long limit) { return (Byte64Mask) VectorSupport.indexPartiallyInUpperRange( - Byte64Mask.class, byte.class, VLENGTH, offset, limit, + Byte64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Byte64Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -691,7 +698,7 @@ final class Byte64Vector extends ByteVector { @ForceInline public Byte64Mask compress() { return (Byte64Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Byte64Vector.class, Byte64Mask.class, ETYPE, VLENGTH, null, this, + Byte64Vector.class, Byte64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -703,7 +710,7 @@ final class Byte64Vector extends ByteVector { public Byte64Mask and(VectorMask mask) { Objects.requireNonNull(mask); Byte64Mask m = (Byte64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Byte64Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Byte64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -713,7 +720,7 @@ final class Byte64Vector extends ByteVector { public Byte64Mask or(VectorMask mask) { Objects.requireNonNull(mask); Byte64Mask m = (Byte64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Byte64Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Byte64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -723,7 +730,7 @@ final class Byte64Vector extends ByteVector { public Byte64Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Byte64Mask m = (Byte64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Byte64Mask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Byte64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -733,21 +740,21 @@ final class Byte64Vector extends ByteVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Byte64Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Byte64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Byte64Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Byte64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Byte64Mask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Byte64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -757,7 +764,7 @@ final class Byte64Vector extends ByteVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Byte64Mask.class, byte.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Byte64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -767,7 +774,7 @@ final class Byte64Vector extends ByteVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Byte64Mask.class, byte.class, VLENGTH, + return VectorSupport.extract(Byte64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -776,7 +783,7 @@ final class Byte64Vector extends ByteVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Byte64Mask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_ne, Byte64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Byte64Mask)m).getBits())); } @@ -784,7 +791,7 @@ final class Byte64Vector extends ByteVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Byte64Mask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_overflow, Byte64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Byte64Mask)m).getBits())); } @@ -792,7 +799,7 @@ final class Byte64Vector extends ByteVector { @ForceInline /*package-private*/ static Byte64Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Byte64Mask.class, byte.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Byte64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java index 3ac62409a95..8c3cc68cece 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteMaxVector.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 @@ -119,6 +119,13 @@ final class ByteMaxVector extends ByteVector { return (byte[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -526,7 +533,7 @@ final class ByteMaxVector extends ByteVector { @ForceInline public byte laneHelper(int i) { return (byte) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { byte[] vecarr = vec.vec(); @@ -546,7 +553,7 @@ final class ByteMaxVector extends ByteVector { @ForceInline public ByteMaxVector withLaneHelper(int i, byte e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { byte[] res = v.vec().clone(); @@ -650,8 +657,8 @@ final class ByteMaxVector extends ByteVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -661,7 +668,7 @@ final class ByteMaxVector extends ByteVector { /*package-private*/ ByteMaxMask indexPartiallyInUpperRange(long offset, long limit) { return (ByteMaxMask) VectorSupport.indexPartiallyInUpperRange( - ByteMaxMask.class, byte.class, VLENGTH, offset, limit, + ByteMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (ByteMaxMask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -677,7 +684,7 @@ final class ByteMaxVector extends ByteVector { @ForceInline public ByteMaxMask compress() { return (ByteMaxMask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - ByteMaxVector.class, ByteMaxMask.class, ETYPE, VLENGTH, null, this, + ByteMaxVector.class, ByteMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -689,7 +696,7 @@ final class ByteMaxVector extends ByteVector { public ByteMaxMask and(VectorMask mask) { Objects.requireNonNull(mask); ByteMaxMask m = (ByteMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, ByteMaxMask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, ByteMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -699,7 +706,7 @@ final class ByteMaxVector extends ByteVector { public ByteMaxMask or(VectorMask mask) { Objects.requireNonNull(mask); ByteMaxMask m = (ByteMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, ByteMaxMask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, ByteMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -709,7 +716,7 @@ final class ByteMaxVector extends ByteVector { public ByteMaxMask xor(VectorMask mask) { Objects.requireNonNull(mask); ByteMaxMask m = (ByteMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, ByteMaxMask.class, null, byte.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, ByteMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -719,21 +726,21 @@ final class ByteMaxVector extends ByteVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, ByteMaxMask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, ByteMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, ByteMaxMask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, ByteMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, ByteMaxMask.class, byte.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, ByteMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -743,7 +750,7 @@ final class ByteMaxVector extends ByteVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, ByteMaxMask.class, byte.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, ByteMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -753,7 +760,7 @@ final class ByteMaxVector extends ByteVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(ByteMaxMask.class, byte.class, VLENGTH, + return VectorSupport.extract(ByteMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -762,7 +769,7 @@ final class ByteMaxVector extends ByteVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, ByteMaxMask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_ne, ByteMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((ByteMaxMask)m).getBits())); } @@ -770,7 +777,7 @@ final class ByteMaxVector extends ByteVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, ByteMaxMask.class, byte.class, VLENGTH, + return VectorSupport.test(BT_overflow, ByteMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((ByteMaxMask)m).getBits())); } @@ -778,7 +785,7 @@ final class ByteMaxVector extends ByteVector { @ForceInline /*package-private*/ static ByteMaxMask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(ByteMaxMask.class, byte.class, VLENGTH, + return VectorSupport.fromBitsCoerced(ByteMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java index c071026a3b3..64d8e3a8252 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java @@ -59,6 +59,10 @@ public abstract class ByteVector extends AbstractVector { static final ValueLayout.OfByte ELEMENT_LAYOUT = ValueLayout.JAVA_BYTE.withByteAlignment(1); + static final int LANE_TYPE_ORDINAL = LT_BYTE; + + static final int LANEBITS_TYPE_ORDINAL = LT_BYTE; + @ForceInline static int opCode(Operator op) { return VectorOperators.opCode(op, VO_OPCODE_VALID, FORBID_OPCODE_KIND); @@ -573,7 +577,7 @@ public abstract class ByteVector extends AbstractVector { @ForceInline public static ByteVector zero(VectorSpecies species) { ByteSpecies vsp = (ByteSpecies) species; - return VectorSupport.fromBitsCoerced(vsp.vectorType(), byte.class, species.length(), + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), 0, MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); } @@ -695,7 +699,7 @@ public abstract class ByteVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), null, byte.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, UN_IMPL.find(op, opc, ByteVector::unaryOperations)); } @@ -723,7 +727,7 @@ public abstract class ByteVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), maskClass, byte.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, UN_IMPL.find(op, opc, ByteVector::unaryOperations)); } @@ -796,7 +800,7 @@ public abstract class ByteVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), null, byte.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, null, BIN_IMPL.find(op, opc, ByteVector::binaryOperations)); } @@ -847,7 +851,7 @@ public abstract class ByteVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), maskClass, byte.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, m, BIN_IMPL.find(op, opc, ByteVector::binaryOperations)); } @@ -1034,7 +1038,7 @@ public abstract class ByteVector extends AbstractVector { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), null, byte.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, e, null, BIN_INT_IMPL.find(op, opc, ByteVector::broadcastIntOperations)); } @@ -1055,7 +1059,7 @@ public abstract class ByteVector extends AbstractVector { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), maskClass, byte.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, e, m, BIN_INT_IMPL.find(op, opc, ByteVector::broadcastIntOperations)); } @@ -1132,7 +1136,7 @@ public abstract class ByteVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), null, byte.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, tother, null, TERN_IMPL.find(op, opc, ByteVector::ternaryOperations)); } @@ -1172,7 +1176,7 @@ public abstract class ByteVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), maskClass, byte.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, tother, m, TERN_IMPL.find(op, opc, ByteVector::ternaryOperations)); } @@ -2070,7 +2074,7 @@ public abstract class ByteVector extends AbstractVector { that.check(this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, byte.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, null, (cond, v0, v1, m1) -> { AbstractMask m @@ -2092,7 +2096,7 @@ public abstract class ByteVector extends AbstractVector { m.check(maskType, this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, byte.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, m, (cond, v0, v1, m1) -> { AbstractMask cmpM @@ -2223,7 +2227,7 @@ public abstract class ByteVector extends AbstractVector { blendTemplate(Class maskType, ByteVector v, M m) { v.check(this); return VectorSupport.blend( - getClass(), maskType, byte.class, length(), + getClass(), maskType, laneTypeOrdinal(), length(), this, v, m, (v0, v1, m_) -> v0.bOp(v1, m_, (i, a, b) -> b)); } @@ -2240,7 +2244,7 @@ public abstract class ByteVector extends AbstractVector { // make sure VLENGTH*scale doesn't overflow: vsp.checkScale(scale); return VectorSupport.indexVector( - getClass(), byte.class, length(), + getClass(), laneTypeOrdinal(), length(), this, scale, vsp, (v, scale_, s) -> { @@ -2432,7 +2436,7 @@ public abstract class ByteVector extends AbstractVector { ByteVector rearrangeTemplate(Class shuffletype, S shuffle) { Objects.requireNonNull(shuffle); return VectorSupport.rearrangeOp( - getClass(), shuffletype, null, byte.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2459,7 +2463,7 @@ public abstract class ByteVector extends AbstractVector { Objects.requireNonNull(shuffle); m.check(masktype, this); return VectorSupport.rearrangeOp( - getClass(), shuffletype, masktype, byte.class, length(), + getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), this, shuffle, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2485,7 +2489,7 @@ public abstract class ByteVector extends AbstractVector { VectorMask valid = shuffle.laneIsValid(); ByteVector r0 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, byte.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v0, s_, m_) -> v0.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); @@ -2493,7 +2497,7 @@ public abstract class ByteVector extends AbstractVector { })); ByteVector r1 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, byte.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), v, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2543,7 +2547,7 @@ public abstract class ByteVector extends AbstractVector { ByteVector compressTemplate(Class masktype, M m) { m.check(masktype, this); return (ByteVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_COMPRESS, getClass(), masktype, - byte.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> compressHelper(v1, m1)); } @@ -2562,7 +2566,7 @@ public abstract class ByteVector extends AbstractVector { ByteVector expandTemplate(Class masktype, M m) { m.check(masktype, this); return (ByteVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_EXPAND, getClass(), masktype, - byte.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> expandHelper(v1, m1)); } @@ -2577,7 +2581,7 @@ public abstract class ByteVector extends AbstractVector { /*package-private*/ @ForceInline final ByteVector selectFromTemplate(ByteVector v) { - return (ByteVector)VectorSupport.selectFromOp(getClass(), null, byte.class, + return (ByteVector)VectorSupport.selectFromOp(getClass(), null, laneTypeOrdinal(), length(), this, v, null, (v1, v2, _m) -> v2.rearrange(v1.toShuffle())); @@ -2597,7 +2601,7 @@ public abstract class ByteVector extends AbstractVector { ByteVector selectFromTemplate(ByteVector v, Class masktype, M m) { m.check(masktype, this); - return (ByteVector)VectorSupport.selectFromOp(getClass(), masktype, byte.class, + return (ByteVector)VectorSupport.selectFromOp(getClass(), masktype, laneTypeOrdinal(), length(), this, v, m, (v1, v2, _m) -> v2.rearrange(v1.toShuffle(), _m)); @@ -2615,7 +2619,7 @@ public abstract class ByteVector extends AbstractVector { /*package-private*/ @ForceInline final ByteVector selectFromTemplate(ByteVector v1, ByteVector v2) { - return VectorSupport.selectFromTwoVectorOp(getClass(), byte.class, length(), this, v1, v2, + return VectorSupport.selectFromTwoVectorOp(getClass(), laneTypeOrdinal(), length(), this, v1, v2, (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); } @@ -2835,7 +2839,7 @@ public abstract class ByteVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), maskClass, byte.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, REDUCE_IMPL.find(op, opc, ByteVector::reductionOperations))); } @@ -2853,7 +2857,7 @@ public abstract class ByteVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), null, byte.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, REDUCE_IMPL.find(op, opc, ByteVector::reductionOperations))); } @@ -3145,7 +3149,7 @@ public abstract class ByteVector extends AbstractVector { } return VectorSupport.loadWithMap( - vectorType, null, byte.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), lsp.vectorType(), lsp.length(), a, ARRAY_BASE, vix0, vix1, vix2, vix3, null, a, offset, indexMap, mapOffset, vsp, @@ -3480,7 +3484,7 @@ public abstract class ByteVector extends AbstractVector { offset = checkFromIndexSize(offset, length(), a.length); ByteSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, @@ -3634,7 +3638,7 @@ public abstract class ByteVector extends AbstractVector { ByteSpecies vsp = vspecies(); ByteVector normalized = this.and((byte) 1); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, booleanArrayAddress(a, offset), false, normalized, a, offset, @@ -3847,7 +3851,7 @@ public abstract class ByteVector extends AbstractVector { ByteVector fromArray0Template(byte[] a, int offset) { ByteSpecies vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -3864,7 +3868,7 @@ public abstract class ByteVector extends AbstractVector { m.check(species()); ByteSpecies vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -3920,7 +3924,7 @@ public abstract class ByteVector extends AbstractVector { } return VectorSupport.loadWithMap( - vectorType, maskClass, byte.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), lsp.vectorType(), lsp.length(), a, ARRAY_BASE, vix0, vix1, vix2, vix3, m, a, offset, indexMap, mapOffset, vsp, @@ -3937,7 +3941,7 @@ public abstract class ByteVector extends AbstractVector { ByteVector fromBooleanArray0Template(boolean[] a, int offset) { ByteSpecies vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, booleanArrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -3954,7 +3958,7 @@ public abstract class ByteVector extends AbstractVector { m.check(species()); ByteSpecies vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, booleanArrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -3968,7 +3972,7 @@ public abstract class ByteVector extends AbstractVector { ByteVector fromMemorySegment0Template(MemorySegment ms, long offset) { ByteSpecies vsp = vspecies(); return ScopedMemoryAccess.loadFromMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, vsp, (msp, off, s) -> { return s.ldLongOp((MemorySegment) msp, off, ByteVector::memorySegmentGet); @@ -3984,7 +3988,7 @@ public abstract class ByteVector extends AbstractVector { ByteSpecies vsp = vspecies(); m.check(vsp); return ScopedMemoryAccess.loadFromMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, m, vsp, offsetInRange, (msp, off, s, vm) -> { return s.ldLongOp((MemorySegment) msp, off, vm, ByteVector::memorySegmentGet); @@ -4002,7 +4006,7 @@ public abstract class ByteVector extends AbstractVector { void intoArray0Template(byte[] a, int offset) { ByteSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, (arr, off, v) @@ -4019,7 +4023,7 @@ public abstract class ByteVector extends AbstractVector { m.check(species()); ByteSpecies vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -4038,7 +4042,7 @@ public abstract class ByteVector extends AbstractVector { ByteSpecies vsp = vspecies(); ByteVector normalized = this.and((byte) 1); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, booleanArrayAddress(a, offset), false, normalized, m, a, offset, (arr, off, v, vm) @@ -4051,7 +4055,7 @@ public abstract class ByteVector extends AbstractVector { void intoMemorySegment0(MemorySegment ms, long offset) { ByteSpecies vsp = vspecies(); ScopedMemoryAccess.storeIntoMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), this, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v) -> { @@ -4068,7 +4072,7 @@ public abstract class ByteVector extends AbstractVector { ByteSpecies vsp = vspecies(); m.check(vsp); ScopedMemoryAccess.storeIntoMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), this, m, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v, vm) -> { @@ -4289,7 +4293,7 @@ public abstract class ByteVector extends AbstractVector { final ByteVector broadcastBits(long bits) { return (ByteVector) VectorSupport.fromBitsCoerced( - vectorType, byte.class, laneCount, + vectorType, laneTypeOrdinal(), laneCount, bits, MODE_BROADCAST, this, (bits_, s_) -> s_.rvOp(i -> bits_)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java index eaf77d59a23..f00efcf5163 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double128Vector.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 @@ -119,6 +119,13 @@ final class Double128Vector extends DoubleVector { return (double[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -516,7 +523,7 @@ final class Double128Vector extends DoubleVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { double[] vecarr = vec.vec(); @@ -537,7 +544,7 @@ final class Double128Vector extends DoubleVector { @ForceInline public Double128Vector withLaneHelper(int i, double e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Double.doubleToRawLongBits(e), (v, ix, bits) -> { double[] res = v.vec().clone(); @@ -641,8 +648,8 @@ final class Double128Vector extends DoubleVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -652,7 +659,7 @@ final class Double128Vector extends DoubleVector { /*package-private*/ Double128Mask indexPartiallyInUpperRange(long offset, long limit) { return (Double128Mask) VectorSupport.indexPartiallyInUpperRange( - Double128Mask.class, double.class, VLENGTH, offset, limit, + Double128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Double128Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -668,7 +675,7 @@ final class Double128Vector extends DoubleVector { @ForceInline public Double128Mask compress() { return (Double128Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Double128Vector.class, Double128Mask.class, ETYPE, VLENGTH, null, this, + Double128Vector.class, Double128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -680,7 +687,7 @@ final class Double128Vector extends DoubleVector { public Double128Mask and(VectorMask mask) { Objects.requireNonNull(mask); Double128Mask m = (Double128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Double128Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Double128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -690,7 +697,7 @@ final class Double128Vector extends DoubleVector { public Double128Mask or(VectorMask mask) { Objects.requireNonNull(mask); Double128Mask m = (Double128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Double128Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Double128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -700,7 +707,7 @@ final class Double128Vector extends DoubleVector { public Double128Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Double128Mask m = (Double128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Double128Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Double128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -710,21 +717,21 @@ final class Double128Vector extends DoubleVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Double128Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Double128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Double128Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Double128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Double128Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Double128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -734,7 +741,7 @@ final class Double128Vector extends DoubleVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Double128Mask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Double128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -744,7 +751,7 @@ final class Double128Vector extends DoubleVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Double128Mask.class, double.class, VLENGTH, + return VectorSupport.extract(Double128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -753,7 +760,7 @@ final class Double128Vector extends DoubleVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Double128Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, Double128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Double128Mask)m).getBits())); } @@ -761,7 +768,7 @@ final class Double128Vector extends DoubleVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Double128Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, Double128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Double128Mask)m).getBits())); } @@ -769,7 +776,7 @@ final class Double128Vector extends DoubleVector { @ForceInline /*package-private*/ static Double128Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Double128Mask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Double128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java index cf9c8794ce4..0f145bf06e2 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double256Vector.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 @@ -119,6 +119,13 @@ final class Double256Vector extends DoubleVector { return (double[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -518,7 +525,7 @@ final class Double256Vector extends DoubleVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { double[] vecarr = vec.vec(); @@ -541,7 +548,7 @@ final class Double256Vector extends DoubleVector { @ForceInline public Double256Vector withLaneHelper(int i, double e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Double.doubleToRawLongBits(e), (v, ix, bits) -> { double[] res = v.vec().clone(); @@ -645,8 +652,8 @@ final class Double256Vector extends DoubleVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -656,7 +663,7 @@ final class Double256Vector extends DoubleVector { /*package-private*/ Double256Mask indexPartiallyInUpperRange(long offset, long limit) { return (Double256Mask) VectorSupport.indexPartiallyInUpperRange( - Double256Mask.class, double.class, VLENGTH, offset, limit, + Double256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Double256Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -672,7 +679,7 @@ final class Double256Vector extends DoubleVector { @ForceInline public Double256Mask compress() { return (Double256Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Double256Vector.class, Double256Mask.class, ETYPE, VLENGTH, null, this, + Double256Vector.class, Double256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -684,7 +691,7 @@ final class Double256Vector extends DoubleVector { public Double256Mask and(VectorMask mask) { Objects.requireNonNull(mask); Double256Mask m = (Double256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Double256Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Double256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -694,7 +701,7 @@ final class Double256Vector extends DoubleVector { public Double256Mask or(VectorMask mask) { Objects.requireNonNull(mask); Double256Mask m = (Double256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Double256Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Double256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -704,7 +711,7 @@ final class Double256Vector extends DoubleVector { public Double256Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Double256Mask m = (Double256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Double256Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Double256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -714,21 +721,21 @@ final class Double256Vector extends DoubleVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Double256Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Double256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Double256Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Double256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Double256Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Double256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -738,7 +745,7 @@ final class Double256Vector extends DoubleVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Double256Mask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Double256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -748,7 +755,7 @@ final class Double256Vector extends DoubleVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Double256Mask.class, double.class, VLENGTH, + return VectorSupport.extract(Double256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -757,7 +764,7 @@ final class Double256Vector extends DoubleVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Double256Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, Double256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Double256Mask)m).getBits())); } @@ -765,7 +772,7 @@ final class Double256Vector extends DoubleVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Double256Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, Double256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Double256Mask)m).getBits())); } @@ -773,7 +780,7 @@ final class Double256Vector extends DoubleVector { @ForceInline /*package-private*/ static Double256Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Double256Mask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Double256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java index a7a86c25841..581a3ac7329 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double512Vector.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 @@ -119,6 +119,13 @@ final class Double512Vector extends DoubleVector { return (double[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -522,7 +529,7 @@ final class Double512Vector extends DoubleVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { double[] vecarr = vec.vec(); @@ -549,7 +556,7 @@ final class Double512Vector extends DoubleVector { @ForceInline public Double512Vector withLaneHelper(int i, double e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Double.doubleToRawLongBits(e), (v, ix, bits) -> { double[] res = v.vec().clone(); @@ -653,8 +660,8 @@ final class Double512Vector extends DoubleVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -664,7 +671,7 @@ final class Double512Vector extends DoubleVector { /*package-private*/ Double512Mask indexPartiallyInUpperRange(long offset, long limit) { return (Double512Mask) VectorSupport.indexPartiallyInUpperRange( - Double512Mask.class, double.class, VLENGTH, offset, limit, + Double512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Double512Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -680,7 +687,7 @@ final class Double512Vector extends DoubleVector { @ForceInline public Double512Mask compress() { return (Double512Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Double512Vector.class, Double512Mask.class, ETYPE, VLENGTH, null, this, + Double512Vector.class, Double512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -692,7 +699,7 @@ final class Double512Vector extends DoubleVector { public Double512Mask and(VectorMask mask) { Objects.requireNonNull(mask); Double512Mask m = (Double512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Double512Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Double512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -702,7 +709,7 @@ final class Double512Vector extends DoubleVector { public Double512Mask or(VectorMask mask) { Objects.requireNonNull(mask); Double512Mask m = (Double512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Double512Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Double512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -712,7 +719,7 @@ final class Double512Vector extends DoubleVector { public Double512Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Double512Mask m = (Double512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Double512Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Double512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -722,21 +729,21 @@ final class Double512Vector extends DoubleVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Double512Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Double512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Double512Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Double512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Double512Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Double512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -746,7 +753,7 @@ final class Double512Vector extends DoubleVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Double512Mask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Double512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -756,7 +763,7 @@ final class Double512Vector extends DoubleVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Double512Mask.class, double.class, VLENGTH, + return VectorSupport.extract(Double512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -765,7 +772,7 @@ final class Double512Vector extends DoubleVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Double512Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, Double512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Double512Mask)m).getBits())); } @@ -773,7 +780,7 @@ final class Double512Vector extends DoubleVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Double512Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, Double512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Double512Mask)m).getBits())); } @@ -781,7 +788,7 @@ final class Double512Vector extends DoubleVector { @ForceInline /*package-private*/ static Double512Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Double512Mask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Double512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java index df6b627cc18..9535f112ada 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Double64Vector.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 @@ -119,6 +119,13 @@ final class Double64Vector extends DoubleVector { return (double[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -515,7 +522,7 @@ final class Double64Vector extends DoubleVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { double[] vecarr = vec.vec(); @@ -535,7 +542,7 @@ final class Double64Vector extends DoubleVector { @ForceInline public Double64Vector withLaneHelper(int i, double e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Double.doubleToRawLongBits(e), (v, ix, bits) -> { double[] res = v.vec().clone(); @@ -639,8 +646,8 @@ final class Double64Vector extends DoubleVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -650,7 +657,7 @@ final class Double64Vector extends DoubleVector { /*package-private*/ Double64Mask indexPartiallyInUpperRange(long offset, long limit) { return (Double64Mask) VectorSupport.indexPartiallyInUpperRange( - Double64Mask.class, double.class, VLENGTH, offset, limit, + Double64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Double64Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -666,7 +673,7 @@ final class Double64Vector extends DoubleVector { @ForceInline public Double64Mask compress() { return (Double64Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Double64Vector.class, Double64Mask.class, ETYPE, VLENGTH, null, this, + Double64Vector.class, Double64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -678,7 +685,7 @@ final class Double64Vector extends DoubleVector { public Double64Mask and(VectorMask mask) { Objects.requireNonNull(mask); Double64Mask m = (Double64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Double64Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Double64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -688,7 +695,7 @@ final class Double64Vector extends DoubleVector { public Double64Mask or(VectorMask mask) { Objects.requireNonNull(mask); Double64Mask m = (Double64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Double64Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Double64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -698,7 +705,7 @@ final class Double64Vector extends DoubleVector { public Double64Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Double64Mask m = (Double64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Double64Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Double64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -708,21 +715,21 @@ final class Double64Vector extends DoubleVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Double64Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Double64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Double64Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Double64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Double64Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Double64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -732,7 +739,7 @@ final class Double64Vector extends DoubleVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Double64Mask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Double64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -742,7 +749,7 @@ final class Double64Vector extends DoubleVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Double64Mask.class, double.class, VLENGTH, + return VectorSupport.extract(Double64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -751,7 +758,7 @@ final class Double64Vector extends DoubleVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Double64Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, Double64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Double64Mask)m).getBits())); } @@ -759,7 +766,7 @@ final class Double64Vector extends DoubleVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Double64Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, Double64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Double64Mask)m).getBits())); } @@ -767,7 +774,7 @@ final class Double64Vector extends DoubleVector { @ForceInline /*package-private*/ static Double64Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Double64Mask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Double64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java index 47be1f609d8..8daa77dfc49 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleMaxVector.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 @@ -119,6 +119,13 @@ final class DoubleMaxVector extends DoubleVector { return (double[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -514,7 +521,7 @@ final class DoubleMaxVector extends DoubleVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { double[] vecarr = vec.vec(); @@ -534,7 +541,7 @@ final class DoubleMaxVector extends DoubleVector { @ForceInline public DoubleMaxVector withLaneHelper(int i, double e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Double.doubleToRawLongBits(e), (v, ix, bits) -> { double[] res = v.vec().clone(); @@ -638,8 +645,8 @@ final class DoubleMaxVector extends DoubleVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -649,7 +656,7 @@ final class DoubleMaxVector extends DoubleVector { /*package-private*/ DoubleMaxMask indexPartiallyInUpperRange(long offset, long limit) { return (DoubleMaxMask) VectorSupport.indexPartiallyInUpperRange( - DoubleMaxMask.class, double.class, VLENGTH, offset, limit, + DoubleMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (DoubleMaxMask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -665,7 +672,7 @@ final class DoubleMaxVector extends DoubleVector { @ForceInline public DoubleMaxMask compress() { return (DoubleMaxMask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - DoubleMaxVector.class, DoubleMaxMask.class, ETYPE, VLENGTH, null, this, + DoubleMaxVector.class, DoubleMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -677,7 +684,7 @@ final class DoubleMaxVector extends DoubleVector { public DoubleMaxMask and(VectorMask mask) { Objects.requireNonNull(mask); DoubleMaxMask m = (DoubleMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, DoubleMaxMask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, DoubleMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -687,7 +694,7 @@ final class DoubleMaxVector extends DoubleVector { public DoubleMaxMask or(VectorMask mask) { Objects.requireNonNull(mask); DoubleMaxMask m = (DoubleMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, DoubleMaxMask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, DoubleMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -697,7 +704,7 @@ final class DoubleMaxVector extends DoubleVector { public DoubleMaxMask xor(VectorMask mask) { Objects.requireNonNull(mask); DoubleMaxMask m = (DoubleMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, DoubleMaxMask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, DoubleMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -707,21 +714,21 @@ final class DoubleMaxVector extends DoubleVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, DoubleMaxMask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, DoubleMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, DoubleMaxMask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, DoubleMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, DoubleMaxMask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, DoubleMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -731,7 +738,7 @@ final class DoubleMaxVector extends DoubleVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, DoubleMaxMask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, DoubleMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -741,7 +748,7 @@ final class DoubleMaxVector extends DoubleVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(DoubleMaxMask.class, double.class, VLENGTH, + return VectorSupport.extract(DoubleMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -750,7 +757,7 @@ final class DoubleMaxVector extends DoubleVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, DoubleMaxMask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, DoubleMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((DoubleMaxMask)m).getBits())); } @@ -758,7 +765,7 @@ final class DoubleMaxVector extends DoubleVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, DoubleMaxMask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, DoubleMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((DoubleMaxMask)m).getBits())); } @@ -766,7 +773,7 @@ final class DoubleMaxVector extends DoubleVector { @ForceInline /*package-private*/ static DoubleMaxMask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(DoubleMaxMask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(DoubleMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java index e280ca4150c..70be5f829f0 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/DoubleVector.java @@ -59,6 +59,10 @@ public abstract class DoubleVector extends AbstractVector { static final ValueLayout.OfDouble ELEMENT_LAYOUT = ValueLayout.JAVA_DOUBLE.withByteAlignment(1); + static final int LANE_TYPE_ORDINAL = LT_DOUBLE; + + static final int LANEBITS_TYPE_ORDINAL = LT_LONG; + @ForceInline static int opCode(Operator op) { return VectorOperators.opCode(op, VO_OPCODE_VALID, FORBID_OPCODE_KIND); @@ -562,7 +566,7 @@ public abstract class DoubleVector extends AbstractVector { @ForceInline public static DoubleVector zero(VectorSpecies species) { DoubleSpecies vsp = (DoubleSpecies) species; - return VectorSupport.fromBitsCoerced(vsp.vectorType(), double.class, species.length(), + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), toBits(0.0f), MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); } @@ -684,7 +688,7 @@ public abstract class DoubleVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), null, double.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, UN_IMPL.find(op, opc, DoubleVector::unaryOperations)); } @@ -712,7 +716,7 @@ public abstract class DoubleVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), maskClass, double.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, UN_IMPL.find(op, opc, DoubleVector::unaryOperations)); } @@ -801,7 +805,7 @@ public abstract class DoubleVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), null, double.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, null, BIN_IMPL.find(op, opc, DoubleVector::binaryOperations)); } @@ -839,7 +843,7 @@ public abstract class DoubleVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), maskClass, double.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, m, BIN_IMPL.find(op, opc, DoubleVector::binaryOperations)); } @@ -1021,7 +1025,7 @@ public abstract class DoubleVector extends AbstractVector { tother.check(this); int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), null, double.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, tother, null, TERN_IMPL.find(op, opc, DoubleVector::ternaryOperations)); } @@ -1056,7 +1060,7 @@ public abstract class DoubleVector extends AbstractVector { int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), maskClass, double.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, tother, m, TERN_IMPL.find(op, opc, DoubleVector::ternaryOperations)); } @@ -1928,7 +1932,7 @@ public abstract class DoubleVector extends AbstractVector { that.check(this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, double.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, null, (cond, v0, v1, m1) -> { AbstractMask m @@ -1950,7 +1954,7 @@ public abstract class DoubleVector extends AbstractVector { m.check(maskType, this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, double.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, m, (cond, v0, v1, m1) -> { AbstractMask cmpM @@ -2077,7 +2081,7 @@ public abstract class DoubleVector extends AbstractVector { blendTemplate(Class maskType, DoubleVector v, M m) { v.check(this); return VectorSupport.blend( - getClass(), maskType, double.class, length(), + getClass(), maskType, laneTypeOrdinal(), length(), this, v, m, (v0, v1, m_) -> v0.bOp(v1, m_, (i, a, b) -> b)); } @@ -2094,7 +2098,7 @@ public abstract class DoubleVector extends AbstractVector { // make sure VLENGTH*scale doesn't overflow: vsp.checkScale(scale); return VectorSupport.indexVector( - getClass(), double.class, length(), + getClass(), laneTypeOrdinal(), length(), this, scale, vsp, (v, scale_, s) -> { @@ -2286,7 +2290,7 @@ public abstract class DoubleVector extends AbstractVector { DoubleVector rearrangeTemplate(Class shuffletype, S shuffle) { Objects.requireNonNull(shuffle); return VectorSupport.rearrangeOp( - getClass(), shuffletype, null, double.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2313,7 +2317,7 @@ public abstract class DoubleVector extends AbstractVector { Objects.requireNonNull(shuffle); m.check(masktype, this); return VectorSupport.rearrangeOp( - getClass(), shuffletype, masktype, double.class, length(), + getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), this, shuffle, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2339,7 +2343,7 @@ public abstract class DoubleVector extends AbstractVector { VectorMask valid = shuffle.laneIsValid(); DoubleVector r0 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, double.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v0, s_, m_) -> v0.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); @@ -2347,7 +2351,7 @@ public abstract class DoubleVector extends AbstractVector { })); DoubleVector r1 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, double.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), v, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2391,7 +2395,7 @@ public abstract class DoubleVector extends AbstractVector { DoubleVector compressTemplate(Class masktype, M m) { m.check(masktype, this); return (DoubleVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_COMPRESS, getClass(), masktype, - double.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> compressHelper(v1, m1)); } @@ -2410,7 +2414,7 @@ public abstract class DoubleVector extends AbstractVector { DoubleVector expandTemplate(Class masktype, M m) { m.check(masktype, this); return (DoubleVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_EXPAND, getClass(), masktype, - double.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> expandHelper(v1, m1)); } @@ -2425,7 +2429,7 @@ public abstract class DoubleVector extends AbstractVector { /*package-private*/ @ForceInline final DoubleVector selectFromTemplate(DoubleVector v) { - return (DoubleVector)VectorSupport.selectFromOp(getClass(), null, double.class, + return (DoubleVector)VectorSupport.selectFromOp(getClass(), null, laneTypeOrdinal(), length(), this, v, null, (v1, v2, _m) -> v2.rearrange(v1.toShuffle())); @@ -2445,7 +2449,7 @@ public abstract class DoubleVector extends AbstractVector { DoubleVector selectFromTemplate(DoubleVector v, Class masktype, M m) { m.check(masktype, this); - return (DoubleVector)VectorSupport.selectFromOp(getClass(), masktype, double.class, + return (DoubleVector)VectorSupport.selectFromOp(getClass(), masktype, laneTypeOrdinal(), length(), this, v, m, (v1, v2, _m) -> v2.rearrange(v1.toShuffle(), _m)); @@ -2463,7 +2467,7 @@ public abstract class DoubleVector extends AbstractVector { /*package-private*/ @ForceInline final DoubleVector selectFromTemplate(DoubleVector v1, DoubleVector v2) { - return VectorSupport.selectFromTwoVectorOp(getClass(), double.class, length(), this, v1, v2, + return VectorSupport.selectFromTwoVectorOp(getClass(), laneTypeOrdinal(), length(), this, v1, v2, (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); } @@ -2661,7 +2665,7 @@ public abstract class DoubleVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), maskClass, double.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, REDUCE_IMPL.find(op, opc, DoubleVector::reductionOperations))); } @@ -2679,7 +2683,7 @@ public abstract class DoubleVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), null, double.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, REDUCE_IMPL.find(op, opc, DoubleVector::reductionOperations))); } @@ -2936,7 +2940,7 @@ public abstract class DoubleVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, null, double.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, null, a, offset, indexMap, mapOffset, vsp, @@ -3119,7 +3123,7 @@ public abstract class DoubleVector extends AbstractVector { offset = checkFromIndexSize(offset, length(), a.length); DoubleSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, @@ -3227,7 +3231,7 @@ public abstract class DoubleVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), null, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, null, @@ -3354,7 +3358,7 @@ public abstract class DoubleVector extends AbstractVector { DoubleVector fromArray0Template(double[] a, int offset) { DoubleSpecies vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -3371,7 +3375,7 @@ public abstract class DoubleVector extends AbstractVector { m.check(species()); DoubleSpecies vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -3422,7 +3426,7 @@ public abstract class DoubleVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, maskClass, double.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, m, a, offset, indexMap, mapOffset, vsp, @@ -3439,7 +3443,7 @@ public abstract class DoubleVector extends AbstractVector { DoubleVector fromMemorySegment0Template(MemorySegment ms, long offset) { DoubleSpecies vsp = vspecies(); return ScopedMemoryAccess.loadFromMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, vsp, (msp, off, s) -> { return s.ldLongOp((MemorySegment) msp, off, DoubleVector::memorySegmentGet); @@ -3455,7 +3459,7 @@ public abstract class DoubleVector extends AbstractVector { DoubleSpecies vsp = vspecies(); m.check(vsp); return ScopedMemoryAccess.loadFromMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, m, vsp, offsetInRange, (msp, off, s, vm) -> { return s.ldLongOp((MemorySegment) msp, off, vm, DoubleVector::memorySegmentGet); @@ -3473,7 +3477,7 @@ public abstract class DoubleVector extends AbstractVector { void intoArray0Template(double[] a, int offset) { DoubleSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, (arr, off, v) @@ -3490,7 +3494,7 @@ public abstract class DoubleVector extends AbstractVector { m.check(species()); DoubleSpecies vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -3538,7 +3542,7 @@ public abstract class DoubleVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, m, @@ -3557,7 +3561,7 @@ public abstract class DoubleVector extends AbstractVector { void intoMemorySegment0(MemorySegment ms, long offset) { DoubleSpecies vsp = vspecies(); ScopedMemoryAccess.storeIntoMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), this, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v) -> { @@ -3574,7 +3578,7 @@ public abstract class DoubleVector extends AbstractVector { DoubleSpecies vsp = vspecies(); m.check(vsp); ScopedMemoryAccess.storeIntoMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), this, m, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v, vm) -> { @@ -3790,7 +3794,7 @@ public abstract class DoubleVector extends AbstractVector { final DoubleVector broadcastBits(long bits) { return (DoubleVector) VectorSupport.fromBitsCoerced( - vectorType, double.class, laneCount, + vectorType, laneTypeOrdinal(), laneCount, bits, MODE_BROADCAST, this, (bits_, s_) -> s_.rvOp(i -> bits_)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java index fdfd234cb47..62c7d535fc7 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float128Vector.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 @@ -119,6 +119,13 @@ final class Float128Vector extends FloatVector { return (float[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -518,7 +525,7 @@ final class Float128Vector extends FloatVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { float[] vecarr = vec.vec(); @@ -541,7 +548,7 @@ final class Float128Vector extends FloatVector { @ForceInline public Float128Vector withLaneHelper(int i, float e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Float.floatToRawIntBits(e), (v, ix, bits) -> { float[] res = v.vec().clone(); @@ -645,8 +652,8 @@ final class Float128Vector extends FloatVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -656,7 +663,7 @@ final class Float128Vector extends FloatVector { /*package-private*/ Float128Mask indexPartiallyInUpperRange(long offset, long limit) { return (Float128Mask) VectorSupport.indexPartiallyInUpperRange( - Float128Mask.class, float.class, VLENGTH, offset, limit, + Float128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Float128Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -672,7 +679,7 @@ final class Float128Vector extends FloatVector { @ForceInline public Float128Mask compress() { return (Float128Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Float128Vector.class, Float128Mask.class, ETYPE, VLENGTH, null, this, + Float128Vector.class, Float128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -684,7 +691,7 @@ final class Float128Vector extends FloatVector { public Float128Mask and(VectorMask mask) { Objects.requireNonNull(mask); Float128Mask m = (Float128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Float128Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Float128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -694,7 +701,7 @@ final class Float128Vector extends FloatVector { public Float128Mask or(VectorMask mask) { Objects.requireNonNull(mask); Float128Mask m = (Float128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Float128Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Float128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -704,7 +711,7 @@ final class Float128Vector extends FloatVector { public Float128Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Float128Mask m = (Float128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Float128Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -714,21 +721,21 @@ final class Float128Vector extends FloatVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float128Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float128Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float128Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -738,7 +745,7 @@ final class Float128Vector extends FloatVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float128Mask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -748,7 +755,7 @@ final class Float128Vector extends FloatVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Float128Mask.class, float.class, VLENGTH, + return VectorSupport.extract(Float128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -757,7 +764,7 @@ final class Float128Vector extends FloatVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Float128Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, Float128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Float128Mask)m).getBits())); } @@ -765,7 +772,7 @@ final class Float128Vector extends FloatVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Float128Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, Float128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Float128Mask)m).getBits())); } @@ -773,7 +780,7 @@ final class Float128Vector extends FloatVector { @ForceInline /*package-private*/ static Float128Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Float128Mask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Float128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java index 2543382ca14..f41194500f9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float256Vector.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 @@ -119,6 +119,13 @@ final class Float256Vector extends FloatVector { return (float[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -522,7 +529,7 @@ final class Float256Vector extends FloatVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { float[] vecarr = vec.vec(); @@ -549,7 +556,7 @@ final class Float256Vector extends FloatVector { @ForceInline public Float256Vector withLaneHelper(int i, float e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Float.floatToRawIntBits(e), (v, ix, bits) -> { float[] res = v.vec().clone(); @@ -653,8 +660,8 @@ final class Float256Vector extends FloatVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -664,7 +671,7 @@ final class Float256Vector extends FloatVector { /*package-private*/ Float256Mask indexPartiallyInUpperRange(long offset, long limit) { return (Float256Mask) VectorSupport.indexPartiallyInUpperRange( - Float256Mask.class, float.class, VLENGTH, offset, limit, + Float256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Float256Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -680,7 +687,7 @@ final class Float256Vector extends FloatVector { @ForceInline public Float256Mask compress() { return (Float256Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Float256Vector.class, Float256Mask.class, ETYPE, VLENGTH, null, this, + Float256Vector.class, Float256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -692,7 +699,7 @@ final class Float256Vector extends FloatVector { public Float256Mask and(VectorMask mask) { Objects.requireNonNull(mask); Float256Mask m = (Float256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Float256Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Float256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -702,7 +709,7 @@ final class Float256Vector extends FloatVector { public Float256Mask or(VectorMask mask) { Objects.requireNonNull(mask); Float256Mask m = (Float256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Float256Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Float256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -712,7 +719,7 @@ final class Float256Vector extends FloatVector { public Float256Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Float256Mask m = (Float256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Float256Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -722,21 +729,21 @@ final class Float256Vector extends FloatVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float256Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float256Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float256Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -746,7 +753,7 @@ final class Float256Vector extends FloatVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float256Mask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -756,7 +763,7 @@ final class Float256Vector extends FloatVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Float256Mask.class, float.class, VLENGTH, + return VectorSupport.extract(Float256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -765,7 +772,7 @@ final class Float256Vector extends FloatVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Float256Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, Float256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Float256Mask)m).getBits())); } @@ -773,7 +780,7 @@ final class Float256Vector extends FloatVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Float256Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, Float256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Float256Mask)m).getBits())); } @@ -781,7 +788,7 @@ final class Float256Vector extends FloatVector { @ForceInline /*package-private*/ static Float256Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Float256Mask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Float256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java index 627b1e0a237..c2fced0fdae 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float512Vector.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 @@ -119,6 +119,13 @@ final class Float512Vector extends FloatVector { return (float[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -530,7 +537,7 @@ final class Float512Vector extends FloatVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { float[] vecarr = vec.vec(); @@ -565,7 +572,7 @@ final class Float512Vector extends FloatVector { @ForceInline public Float512Vector withLaneHelper(int i, float e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Float.floatToRawIntBits(e), (v, ix, bits) -> { float[] res = v.vec().clone(); @@ -669,8 +676,8 @@ final class Float512Vector extends FloatVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -680,7 +687,7 @@ final class Float512Vector extends FloatVector { /*package-private*/ Float512Mask indexPartiallyInUpperRange(long offset, long limit) { return (Float512Mask) VectorSupport.indexPartiallyInUpperRange( - Float512Mask.class, float.class, VLENGTH, offset, limit, + Float512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Float512Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -696,7 +703,7 @@ final class Float512Vector extends FloatVector { @ForceInline public Float512Mask compress() { return (Float512Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Float512Vector.class, Float512Mask.class, ETYPE, VLENGTH, null, this, + Float512Vector.class, Float512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -708,7 +715,7 @@ final class Float512Vector extends FloatVector { public Float512Mask and(VectorMask mask) { Objects.requireNonNull(mask); Float512Mask m = (Float512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Float512Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Float512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -718,7 +725,7 @@ final class Float512Vector extends FloatVector { public Float512Mask or(VectorMask mask) { Objects.requireNonNull(mask); Float512Mask m = (Float512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Float512Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Float512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -728,7 +735,7 @@ final class Float512Vector extends FloatVector { public Float512Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Float512Mask m = (Float512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Float512Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -738,21 +745,21 @@ final class Float512Vector extends FloatVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float512Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float512Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float512Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -762,7 +769,7 @@ final class Float512Vector extends FloatVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float512Mask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -772,7 +779,7 @@ final class Float512Vector extends FloatVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Float512Mask.class, float.class, VLENGTH, + return VectorSupport.extract(Float512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -781,7 +788,7 @@ final class Float512Vector extends FloatVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Float512Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, Float512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Float512Mask)m).getBits())); } @@ -789,7 +796,7 @@ final class Float512Vector extends FloatVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Float512Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, Float512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Float512Mask)m).getBits())); } @@ -797,7 +804,7 @@ final class Float512Vector extends FloatVector { @ForceInline /*package-private*/ static Float512Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Float512Mask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Float512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java index 3360fdb537a..67676c828d6 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float64Vector.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 @@ -119,6 +119,13 @@ final class Float64Vector extends FloatVector { return (float[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -516,7 +523,7 @@ final class Float64Vector extends FloatVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { float[] vecarr = vec.vec(); @@ -537,7 +544,7 @@ final class Float64Vector extends FloatVector { @ForceInline public Float64Vector withLaneHelper(int i, float e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Float.floatToRawIntBits(e), (v, ix, bits) -> { float[] res = v.vec().clone(); @@ -641,8 +648,8 @@ final class Float64Vector extends FloatVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -652,7 +659,7 @@ final class Float64Vector extends FloatVector { /*package-private*/ Float64Mask indexPartiallyInUpperRange(long offset, long limit) { return (Float64Mask) VectorSupport.indexPartiallyInUpperRange( - Float64Mask.class, float.class, VLENGTH, offset, limit, + Float64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Float64Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -668,7 +675,7 @@ final class Float64Vector extends FloatVector { @ForceInline public Float64Mask compress() { return (Float64Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Float64Vector.class, Float64Mask.class, ETYPE, VLENGTH, null, this, + Float64Vector.class, Float64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -680,7 +687,7 @@ final class Float64Vector extends FloatVector { public Float64Mask and(VectorMask mask) { Objects.requireNonNull(mask); Float64Mask m = (Float64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Float64Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Float64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -690,7 +697,7 @@ final class Float64Vector extends FloatVector { public Float64Mask or(VectorMask mask) { Objects.requireNonNull(mask); Float64Mask m = (Float64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Float64Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Float64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -700,7 +707,7 @@ final class Float64Vector extends FloatVector { public Float64Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Float64Mask m = (Float64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Float64Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Float64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -710,21 +717,21 @@ final class Float64Vector extends FloatVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float64Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Float64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float64Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Float64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float64Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Float64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -734,7 +741,7 @@ final class Float64Vector extends FloatVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float64Mask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Float64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -744,7 +751,7 @@ final class Float64Vector extends FloatVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Float64Mask.class, float.class, VLENGTH, + return VectorSupport.extract(Float64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -753,7 +760,7 @@ final class Float64Vector extends FloatVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Float64Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, Float64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Float64Mask)m).getBits())); } @@ -761,7 +768,7 @@ final class Float64Vector extends FloatVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Float64Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, Float64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Float64Mask)m).getBits())); } @@ -769,7 +776,7 @@ final class Float64Vector extends FloatVector { @ForceInline /*package-private*/ static Float64Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Float64Mask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Float64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java index 4a72661ce8b..35e4c83ebde 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatMaxVector.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 @@ -119,6 +119,13 @@ final class FloatMaxVector extends FloatVector { return (float[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -514,7 +521,7 @@ final class FloatMaxVector extends FloatVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { float[] vecarr = vec.vec(); @@ -534,7 +541,7 @@ final class FloatMaxVector extends FloatVector { @ForceInline public FloatMaxVector withLaneHelper(int i, float e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)Float.floatToRawIntBits(e), (v, ix, bits) -> { float[] res = v.vec().clone(); @@ -638,8 +645,8 @@ final class FloatMaxVector extends FloatVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -649,7 +656,7 @@ final class FloatMaxVector extends FloatVector { /*package-private*/ FloatMaxMask indexPartiallyInUpperRange(long offset, long limit) { return (FloatMaxMask) VectorSupport.indexPartiallyInUpperRange( - FloatMaxMask.class, float.class, VLENGTH, offset, limit, + FloatMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (FloatMaxMask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -665,7 +672,7 @@ final class FloatMaxVector extends FloatVector { @ForceInline public FloatMaxMask compress() { return (FloatMaxMask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - FloatMaxVector.class, FloatMaxMask.class, ETYPE, VLENGTH, null, this, + FloatMaxVector.class, FloatMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -677,7 +684,7 @@ final class FloatMaxVector extends FloatVector { public FloatMaxMask and(VectorMask mask) { Objects.requireNonNull(mask); FloatMaxMask m = (FloatMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, FloatMaxMask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, FloatMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -687,7 +694,7 @@ final class FloatMaxVector extends FloatVector { public FloatMaxMask or(VectorMask mask) { Objects.requireNonNull(mask); FloatMaxMask m = (FloatMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, FloatMaxMask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, FloatMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -697,7 +704,7 @@ final class FloatMaxVector extends FloatVector { public FloatMaxMask xor(VectorMask mask) { Objects.requireNonNull(mask); FloatMaxMask m = (FloatMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, FloatMaxMask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, FloatMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -707,21 +714,21 @@ final class FloatMaxVector extends FloatVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, FloatMaxMask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, FloatMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, FloatMaxMask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, FloatMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, FloatMaxMask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, FloatMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -731,7 +738,7 @@ final class FloatMaxVector extends FloatVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, FloatMaxMask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, FloatMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -741,7 +748,7 @@ final class FloatMaxVector extends FloatVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(FloatMaxMask.class, float.class, VLENGTH, + return VectorSupport.extract(FloatMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -750,7 +757,7 @@ final class FloatMaxVector extends FloatVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, FloatMaxMask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, FloatMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((FloatMaxMask)m).getBits())); } @@ -758,7 +765,7 @@ final class FloatMaxVector extends FloatVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, FloatMaxMask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, FloatMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((FloatMaxMask)m).getBits())); } @@ -766,7 +773,7 @@ final class FloatMaxVector extends FloatVector { @ForceInline /*package-private*/ static FloatMaxMask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(FloatMaxMask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(FloatMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java index 35a8f4a78cf..ee9cb9119fd 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/FloatVector.java @@ -59,6 +59,10 @@ public abstract class FloatVector extends AbstractVector { static final ValueLayout.OfFloat ELEMENT_LAYOUT = ValueLayout.JAVA_FLOAT.withByteAlignment(1); + static final int LANE_TYPE_ORDINAL = LT_FLOAT; + + static final int LANEBITS_TYPE_ORDINAL = LT_INT; + @ForceInline static int opCode(Operator op) { return VectorOperators.opCode(op, VO_OPCODE_VALID, FORBID_OPCODE_KIND); @@ -562,7 +566,7 @@ public abstract class FloatVector extends AbstractVector { @ForceInline public static FloatVector zero(VectorSpecies species) { FloatSpecies vsp = (FloatSpecies) species; - return VectorSupport.fromBitsCoerced(vsp.vectorType(), float.class, species.length(), + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), toBits(0.0f), MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); } @@ -684,7 +688,7 @@ public abstract class FloatVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), null, float.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, UN_IMPL.find(op, opc, FloatVector::unaryOperations)); } @@ -712,7 +716,7 @@ public abstract class FloatVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), maskClass, float.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, UN_IMPL.find(op, opc, FloatVector::unaryOperations)); } @@ -801,7 +805,7 @@ public abstract class FloatVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), null, float.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, null, BIN_IMPL.find(op, opc, FloatVector::binaryOperations)); } @@ -839,7 +843,7 @@ public abstract class FloatVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), maskClass, float.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, m, BIN_IMPL.find(op, opc, FloatVector::binaryOperations)); } @@ -1021,7 +1025,7 @@ public abstract class FloatVector extends AbstractVector { tother.check(this); int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), null, float.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, tother, null, TERN_IMPL.find(op, opc, FloatVector::ternaryOperations)); } @@ -1056,7 +1060,7 @@ public abstract class FloatVector extends AbstractVector { int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), maskClass, float.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, tother, m, TERN_IMPL.find(op, opc, FloatVector::ternaryOperations)); } @@ -1940,7 +1944,7 @@ public abstract class FloatVector extends AbstractVector { that.check(this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, float.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, null, (cond, v0, v1, m1) -> { AbstractMask m @@ -1962,7 +1966,7 @@ public abstract class FloatVector extends AbstractVector { m.check(maskType, this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, float.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, m, (cond, v0, v1, m1) -> { AbstractMask cmpM @@ -2089,7 +2093,7 @@ public abstract class FloatVector extends AbstractVector { blendTemplate(Class maskType, FloatVector v, M m) { v.check(this); return VectorSupport.blend( - getClass(), maskType, float.class, length(), + getClass(), maskType, laneTypeOrdinal(), length(), this, v, m, (v0, v1, m_) -> v0.bOp(v1, m_, (i, a, b) -> b)); } @@ -2106,7 +2110,7 @@ public abstract class FloatVector extends AbstractVector { // make sure VLENGTH*scale doesn't overflow: vsp.checkScale(scale); return VectorSupport.indexVector( - getClass(), float.class, length(), + getClass(), laneTypeOrdinal(), length(), this, scale, vsp, (v, scale_, s) -> { @@ -2298,7 +2302,7 @@ public abstract class FloatVector extends AbstractVector { FloatVector rearrangeTemplate(Class shuffletype, S shuffle) { Objects.requireNonNull(shuffle); return VectorSupport.rearrangeOp( - getClass(), shuffletype, null, float.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2325,7 +2329,7 @@ public abstract class FloatVector extends AbstractVector { Objects.requireNonNull(shuffle); m.check(masktype, this); return VectorSupport.rearrangeOp( - getClass(), shuffletype, masktype, float.class, length(), + getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), this, shuffle, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2351,7 +2355,7 @@ public abstract class FloatVector extends AbstractVector { VectorMask valid = shuffle.laneIsValid(); FloatVector r0 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, float.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v0, s_, m_) -> v0.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); @@ -2359,7 +2363,7 @@ public abstract class FloatVector extends AbstractVector { })); FloatVector r1 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, float.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), v, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2403,7 +2407,7 @@ public abstract class FloatVector extends AbstractVector { FloatVector compressTemplate(Class masktype, M m) { m.check(masktype, this); return (FloatVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_COMPRESS, getClass(), masktype, - float.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> compressHelper(v1, m1)); } @@ -2422,7 +2426,7 @@ public abstract class FloatVector extends AbstractVector { FloatVector expandTemplate(Class masktype, M m) { m.check(masktype, this); return (FloatVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_EXPAND, getClass(), masktype, - float.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> expandHelper(v1, m1)); } @@ -2437,7 +2441,7 @@ public abstract class FloatVector extends AbstractVector { /*package-private*/ @ForceInline final FloatVector selectFromTemplate(FloatVector v) { - return (FloatVector)VectorSupport.selectFromOp(getClass(), null, float.class, + return (FloatVector)VectorSupport.selectFromOp(getClass(), null, laneTypeOrdinal(), length(), this, v, null, (v1, v2, _m) -> v2.rearrange(v1.toShuffle())); @@ -2457,7 +2461,7 @@ public abstract class FloatVector extends AbstractVector { FloatVector selectFromTemplate(FloatVector v, Class masktype, M m) { m.check(masktype, this); - return (FloatVector)VectorSupport.selectFromOp(getClass(), masktype, float.class, + return (FloatVector)VectorSupport.selectFromOp(getClass(), masktype, laneTypeOrdinal(), length(), this, v, m, (v1, v2, _m) -> v2.rearrange(v1.toShuffle(), _m)); @@ -2475,7 +2479,7 @@ public abstract class FloatVector extends AbstractVector { /*package-private*/ @ForceInline final FloatVector selectFromTemplate(FloatVector v1, FloatVector v2) { - return VectorSupport.selectFromTwoVectorOp(getClass(), float.class, length(), this, v1, v2, + return VectorSupport.selectFromTwoVectorOp(getClass(), laneTypeOrdinal(), length(), this, v1, v2, (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); } @@ -2681,7 +2685,7 @@ public abstract class FloatVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), maskClass, float.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, REDUCE_IMPL.find(op, opc, FloatVector::reductionOperations))); } @@ -2699,7 +2703,7 @@ public abstract class FloatVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), null, float.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, REDUCE_IMPL.find(op, opc, FloatVector::reductionOperations))); } @@ -2942,7 +2946,7 @@ public abstract class FloatVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, null, float.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, null, a, offset, indexMap, mapOffset, vsp, @@ -3125,7 +3129,7 @@ public abstract class FloatVector extends AbstractVector { offset = checkFromIndexSize(offset, length(), a.length); FloatSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, @@ -3214,7 +3218,7 @@ public abstract class FloatVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), null, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, null, @@ -3341,7 +3345,7 @@ public abstract class FloatVector extends AbstractVector { FloatVector fromArray0Template(float[] a, int offset) { FloatSpecies vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -3358,7 +3362,7 @@ public abstract class FloatVector extends AbstractVector { m.check(species()); FloatSpecies vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -3391,7 +3395,7 @@ public abstract class FloatVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, maskClass, float.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, m, a, offset, indexMap, mapOffset, vsp, @@ -3408,7 +3412,7 @@ public abstract class FloatVector extends AbstractVector { FloatVector fromMemorySegment0Template(MemorySegment ms, long offset) { FloatSpecies vsp = vspecies(); return ScopedMemoryAccess.loadFromMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, vsp, (msp, off, s) -> { return s.ldLongOp((MemorySegment) msp, off, FloatVector::memorySegmentGet); @@ -3424,7 +3428,7 @@ public abstract class FloatVector extends AbstractVector { FloatSpecies vsp = vspecies(); m.check(vsp); return ScopedMemoryAccess.loadFromMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, m, vsp, offsetInRange, (msp, off, s, vm) -> { return s.ldLongOp((MemorySegment) msp, off, vm, FloatVector::memorySegmentGet); @@ -3442,7 +3446,7 @@ public abstract class FloatVector extends AbstractVector { void intoArray0Template(float[] a, int offset) { FloatSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, (arr, off, v) @@ -3459,7 +3463,7 @@ public abstract class FloatVector extends AbstractVector { m.check(species()); FloatSpecies vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -3488,7 +3492,7 @@ public abstract class FloatVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, m, @@ -3507,7 +3511,7 @@ public abstract class FloatVector extends AbstractVector { void intoMemorySegment0(MemorySegment ms, long offset) { FloatSpecies vsp = vspecies(); ScopedMemoryAccess.storeIntoMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), this, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v) -> { @@ -3524,7 +3528,7 @@ public abstract class FloatVector extends AbstractVector { FloatSpecies vsp = vspecies(); m.check(vsp); ScopedMemoryAccess.storeIntoMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), this, m, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v, vm) -> { @@ -3740,7 +3744,7 @@ public abstract class FloatVector extends AbstractVector { final FloatVector broadcastBits(long bits) { return (FloatVector) VectorSupport.fromBitsCoerced( - vectorType, float.class, laneCount, + vectorType, laneTypeOrdinal(), laneCount, bits, MODE_BROADCAST, this, (bits_, s_) -> s_.rvOp(i -> bits_)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java index edf45b8772a..17e93dbd06a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int128Vector.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 @@ -119,6 +119,13 @@ final class Int128Vector extends IntVector { return (int[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -529,7 +536,7 @@ final class Int128Vector extends IntVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { int[] vecarr = vec.vec(); @@ -552,7 +559,7 @@ final class Int128Vector extends IntVector { @ForceInline public Int128Vector withLaneHelper(int i, int e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { int[] res = v.vec().clone(); @@ -656,8 +663,8 @@ final class Int128Vector extends IntVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -667,7 +674,7 @@ final class Int128Vector extends IntVector { /*package-private*/ Int128Mask indexPartiallyInUpperRange(long offset, long limit) { return (Int128Mask) VectorSupport.indexPartiallyInUpperRange( - Int128Mask.class, int.class, VLENGTH, offset, limit, + Int128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Int128Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -683,7 +690,7 @@ final class Int128Vector extends IntVector { @ForceInline public Int128Mask compress() { return (Int128Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Int128Vector.class, Int128Mask.class, ETYPE, VLENGTH, null, this, + Int128Vector.class, Int128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -695,7 +702,7 @@ final class Int128Vector extends IntVector { public Int128Mask and(VectorMask mask) { Objects.requireNonNull(mask); Int128Mask m = (Int128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Int128Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Int128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -705,7 +712,7 @@ final class Int128Vector extends IntVector { public Int128Mask or(VectorMask mask) { Objects.requireNonNull(mask); Int128Mask m = (Int128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Int128Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Int128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -715,7 +722,7 @@ final class Int128Vector extends IntVector { public Int128Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Int128Mask m = (Int128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Int128Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Int128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -725,21 +732,21 @@ final class Int128Vector extends IntVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Int128Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Int128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Int128Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Int128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Int128Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Int128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -749,7 +756,7 @@ final class Int128Vector extends IntVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Int128Mask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Int128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -759,7 +766,7 @@ final class Int128Vector extends IntVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Int128Mask.class, int.class, VLENGTH, + return VectorSupport.extract(Int128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -768,7 +775,7 @@ final class Int128Vector extends IntVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Int128Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, Int128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Int128Mask)m).getBits())); } @@ -776,7 +783,7 @@ final class Int128Vector extends IntVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Int128Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, Int128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Int128Mask)m).getBits())); } @@ -784,7 +791,7 @@ final class Int128Vector extends IntVector { @ForceInline /*package-private*/ static Int128Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Int128Mask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Int128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java index bb86ede05e1..f9700fbfd71 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int256Vector.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 @@ -119,6 +119,13 @@ final class Int256Vector extends IntVector { return (int[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -533,7 +540,7 @@ final class Int256Vector extends IntVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { int[] vecarr = vec.vec(); @@ -560,7 +567,7 @@ final class Int256Vector extends IntVector { @ForceInline public Int256Vector withLaneHelper(int i, int e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { int[] res = v.vec().clone(); @@ -664,8 +671,8 @@ final class Int256Vector extends IntVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -675,7 +682,7 @@ final class Int256Vector extends IntVector { /*package-private*/ Int256Mask indexPartiallyInUpperRange(long offset, long limit) { return (Int256Mask) VectorSupport.indexPartiallyInUpperRange( - Int256Mask.class, int.class, VLENGTH, offset, limit, + Int256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Int256Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -691,7 +698,7 @@ final class Int256Vector extends IntVector { @ForceInline public Int256Mask compress() { return (Int256Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Int256Vector.class, Int256Mask.class, ETYPE, VLENGTH, null, this, + Int256Vector.class, Int256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -703,7 +710,7 @@ final class Int256Vector extends IntVector { public Int256Mask and(VectorMask mask) { Objects.requireNonNull(mask); Int256Mask m = (Int256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Int256Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Int256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -713,7 +720,7 @@ final class Int256Vector extends IntVector { public Int256Mask or(VectorMask mask) { Objects.requireNonNull(mask); Int256Mask m = (Int256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Int256Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Int256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -723,7 +730,7 @@ final class Int256Vector extends IntVector { public Int256Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Int256Mask m = (Int256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Int256Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Int256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -733,21 +740,21 @@ final class Int256Vector extends IntVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Int256Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Int256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Int256Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Int256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Int256Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Int256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -757,7 +764,7 @@ final class Int256Vector extends IntVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Int256Mask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Int256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -767,7 +774,7 @@ final class Int256Vector extends IntVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Int256Mask.class, int.class, VLENGTH, + return VectorSupport.extract(Int256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -776,7 +783,7 @@ final class Int256Vector extends IntVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Int256Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, Int256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Int256Mask)m).getBits())); } @@ -784,7 +791,7 @@ final class Int256Vector extends IntVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Int256Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, Int256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Int256Mask)m).getBits())); } @@ -792,7 +799,7 @@ final class Int256Vector extends IntVector { @ForceInline /*package-private*/ static Int256Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Int256Mask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Int256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java index 16d3b673da0..2e2ee7eac05 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int512Vector.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 @@ -119,6 +119,13 @@ final class Int512Vector extends IntVector { return (int[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -541,7 +548,7 @@ final class Int512Vector extends IntVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { int[] vecarr = vec.vec(); @@ -576,7 +583,7 @@ final class Int512Vector extends IntVector { @ForceInline public Int512Vector withLaneHelper(int i, int e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { int[] res = v.vec().clone(); @@ -680,8 +687,8 @@ final class Int512Vector extends IntVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -691,7 +698,7 @@ final class Int512Vector extends IntVector { /*package-private*/ Int512Mask indexPartiallyInUpperRange(long offset, long limit) { return (Int512Mask) VectorSupport.indexPartiallyInUpperRange( - Int512Mask.class, int.class, VLENGTH, offset, limit, + Int512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Int512Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -707,7 +714,7 @@ final class Int512Vector extends IntVector { @ForceInline public Int512Mask compress() { return (Int512Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Int512Vector.class, Int512Mask.class, ETYPE, VLENGTH, null, this, + Int512Vector.class, Int512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -719,7 +726,7 @@ final class Int512Vector extends IntVector { public Int512Mask and(VectorMask mask) { Objects.requireNonNull(mask); Int512Mask m = (Int512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Int512Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Int512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -729,7 +736,7 @@ final class Int512Vector extends IntVector { public Int512Mask or(VectorMask mask) { Objects.requireNonNull(mask); Int512Mask m = (Int512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Int512Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Int512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -739,7 +746,7 @@ final class Int512Vector extends IntVector { public Int512Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Int512Mask m = (Int512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Int512Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Int512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -749,21 +756,21 @@ final class Int512Vector extends IntVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Int512Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Int512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Int512Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Int512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Int512Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Int512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -773,7 +780,7 @@ final class Int512Vector extends IntVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Int512Mask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Int512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -783,7 +790,7 @@ final class Int512Vector extends IntVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Int512Mask.class, int.class, VLENGTH, + return VectorSupport.extract(Int512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -792,7 +799,7 @@ final class Int512Vector extends IntVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Int512Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, Int512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Int512Mask)m).getBits())); } @@ -800,7 +807,7 @@ final class Int512Vector extends IntVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Int512Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, Int512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Int512Mask)m).getBits())); } @@ -808,7 +815,7 @@ final class Int512Vector extends IntVector { @ForceInline /*package-private*/ static Int512Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Int512Mask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Int512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java index d55364a8d67..8338799c61a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Int64Vector.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 @@ -119,6 +119,13 @@ final class Int64Vector extends IntVector { return (int[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -527,7 +534,7 @@ final class Int64Vector extends IntVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { int[] vecarr = vec.vec(); @@ -548,7 +555,7 @@ final class Int64Vector extends IntVector { @ForceInline public Int64Vector withLaneHelper(int i, int e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { int[] res = v.vec().clone(); @@ -652,8 +659,8 @@ final class Int64Vector extends IntVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -663,7 +670,7 @@ final class Int64Vector extends IntVector { /*package-private*/ Int64Mask indexPartiallyInUpperRange(long offset, long limit) { return (Int64Mask) VectorSupport.indexPartiallyInUpperRange( - Int64Mask.class, int.class, VLENGTH, offset, limit, + Int64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Int64Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -679,7 +686,7 @@ final class Int64Vector extends IntVector { @ForceInline public Int64Mask compress() { return (Int64Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Int64Vector.class, Int64Mask.class, ETYPE, VLENGTH, null, this, + Int64Vector.class, Int64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -691,7 +698,7 @@ final class Int64Vector extends IntVector { public Int64Mask and(VectorMask mask) { Objects.requireNonNull(mask); Int64Mask m = (Int64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Int64Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Int64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -701,7 +708,7 @@ final class Int64Vector extends IntVector { public Int64Mask or(VectorMask mask) { Objects.requireNonNull(mask); Int64Mask m = (Int64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Int64Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Int64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -711,7 +718,7 @@ final class Int64Vector extends IntVector { public Int64Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Int64Mask m = (Int64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Int64Mask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Int64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -721,21 +728,21 @@ final class Int64Vector extends IntVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Int64Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Int64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Int64Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Int64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Int64Mask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Int64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -745,7 +752,7 @@ final class Int64Vector extends IntVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Int64Mask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Int64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -755,7 +762,7 @@ final class Int64Vector extends IntVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Int64Mask.class, int.class, VLENGTH, + return VectorSupport.extract(Int64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -764,7 +771,7 @@ final class Int64Vector extends IntVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Int64Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, Int64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Int64Mask)m).getBits())); } @@ -772,7 +779,7 @@ final class Int64Vector extends IntVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Int64Mask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, Int64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Int64Mask)m).getBits())); } @@ -780,7 +787,7 @@ final class Int64Vector extends IntVector { @ForceInline /*package-private*/ static Int64Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Int64Mask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Int64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java index 0a1cd45eb93..177890e765c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntMaxVector.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 @@ -119,6 +119,13 @@ final class IntMaxVector extends IntVector { return (int[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -526,7 +533,7 @@ final class IntMaxVector extends IntVector { @ForceInline public int laneHelper(int i) { return (int) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { int[] vecarr = vec.vec(); @@ -546,7 +553,7 @@ final class IntMaxVector extends IntVector { @ForceInline public IntMaxVector withLaneHelper(int i, int e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { int[] res = v.vec().clone(); @@ -650,8 +657,8 @@ final class IntMaxVector extends IntVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -661,7 +668,7 @@ final class IntMaxVector extends IntVector { /*package-private*/ IntMaxMask indexPartiallyInUpperRange(long offset, long limit) { return (IntMaxMask) VectorSupport.indexPartiallyInUpperRange( - IntMaxMask.class, int.class, VLENGTH, offset, limit, + IntMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (IntMaxMask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -677,7 +684,7 @@ final class IntMaxVector extends IntVector { @ForceInline public IntMaxMask compress() { return (IntMaxMask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - IntMaxVector.class, IntMaxMask.class, ETYPE, VLENGTH, null, this, + IntMaxVector.class, IntMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -689,7 +696,7 @@ final class IntMaxVector extends IntVector { public IntMaxMask and(VectorMask mask) { Objects.requireNonNull(mask); IntMaxMask m = (IntMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, IntMaxMask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, IntMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -699,7 +706,7 @@ final class IntMaxVector extends IntVector { public IntMaxMask or(VectorMask mask) { Objects.requireNonNull(mask); IntMaxMask m = (IntMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, IntMaxMask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, IntMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -709,7 +716,7 @@ final class IntMaxVector extends IntVector { public IntMaxMask xor(VectorMask mask) { Objects.requireNonNull(mask); IntMaxMask m = (IntMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, IntMaxMask.class, null, int.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, IntMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -719,21 +726,21 @@ final class IntMaxVector extends IntVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, IntMaxMask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, IntMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, IntMaxMask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, IntMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, IntMaxMask.class, int.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, IntMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -743,7 +750,7 @@ final class IntMaxVector extends IntVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, IntMaxMask.class, int.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, IntMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -753,7 +760,7 @@ final class IntMaxVector extends IntVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(IntMaxMask.class, int.class, VLENGTH, + return VectorSupport.extract(IntMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -762,7 +769,7 @@ final class IntMaxVector extends IntVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, IntMaxMask.class, int.class, VLENGTH, + return VectorSupport.test(BT_ne, IntMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((IntMaxMask)m).getBits())); } @@ -770,7 +777,7 @@ final class IntMaxVector extends IntVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, IntMaxMask.class, int.class, VLENGTH, + return VectorSupport.test(BT_overflow, IntMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((IntMaxMask)m).getBits())); } @@ -778,7 +785,7 @@ final class IntMaxVector extends IntVector { @ForceInline /*package-private*/ static IntMaxMask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(IntMaxMask.class, int.class, VLENGTH, + return VectorSupport.fromBitsCoerced(IntMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java index f340aed4fce..412ac8e59b2 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/IntVector.java @@ -59,6 +59,10 @@ public abstract class IntVector extends AbstractVector { static final ValueLayout.OfInt ELEMENT_LAYOUT = ValueLayout.JAVA_INT.withByteAlignment(1); + static final int LANE_TYPE_ORDINAL = LT_INT; + + static final int LANEBITS_TYPE_ORDINAL = LT_INT; + @ForceInline static int opCode(Operator op) { return VectorOperators.opCode(op, VO_OPCODE_VALID, FORBID_OPCODE_KIND); @@ -573,7 +577,7 @@ public abstract class IntVector extends AbstractVector { @ForceInline public static IntVector zero(VectorSpecies species) { IntSpecies vsp = (IntSpecies) species; - return VectorSupport.fromBitsCoerced(vsp.vectorType(), int.class, species.length(), + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), 0, MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); } @@ -695,7 +699,7 @@ public abstract class IntVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), null, int.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, UN_IMPL.find(op, opc, IntVector::unaryOperations)); } @@ -723,7 +727,7 @@ public abstract class IntVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), maskClass, int.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, UN_IMPL.find(op, opc, IntVector::unaryOperations)); } @@ -796,7 +800,7 @@ public abstract class IntVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), null, int.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, null, BIN_IMPL.find(op, opc, IntVector::binaryOperations)); } @@ -847,7 +851,7 @@ public abstract class IntVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), maskClass, int.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, m, BIN_IMPL.find(op, opc, IntVector::binaryOperations)); } @@ -1038,7 +1042,7 @@ public abstract class IntVector extends AbstractVector { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), null, int.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, e, null, BIN_INT_IMPL.find(op, opc, IntVector::broadcastIntOperations)); } @@ -1059,7 +1063,7 @@ public abstract class IntVector extends AbstractVector { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), maskClass, int.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, e, m, BIN_INT_IMPL.find(op, opc, IntVector::broadcastIntOperations)); } @@ -1135,7 +1139,7 @@ public abstract class IntVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), null, int.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, tother, null, TERN_IMPL.find(op, opc, IntVector::ternaryOperations)); } @@ -1175,7 +1179,7 @@ public abstract class IntVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), maskClass, int.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, tother, m, TERN_IMPL.find(op, opc, IntVector::ternaryOperations)); } @@ -2055,7 +2059,7 @@ public abstract class IntVector extends AbstractVector { that.check(this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, int.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, null, (cond, v0, v1, m1) -> { AbstractMask m @@ -2077,7 +2081,7 @@ public abstract class IntVector extends AbstractVector { m.check(maskType, this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, int.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, m, (cond, v0, v1, m1) -> { AbstractMask cmpM @@ -2208,7 +2212,7 @@ public abstract class IntVector extends AbstractVector { blendTemplate(Class maskType, IntVector v, M m) { v.check(this); return VectorSupport.blend( - getClass(), maskType, int.class, length(), + getClass(), maskType, laneTypeOrdinal(), length(), this, v, m, (v0, v1, m_) -> v0.bOp(v1, m_, (i, a, b) -> b)); } @@ -2225,7 +2229,7 @@ public abstract class IntVector extends AbstractVector { // make sure VLENGTH*scale doesn't overflow: vsp.checkScale(scale); return VectorSupport.indexVector( - getClass(), int.class, length(), + getClass(), laneTypeOrdinal(), length(), this, scale, vsp, (v, scale_, s) -> { @@ -2417,7 +2421,7 @@ public abstract class IntVector extends AbstractVector { IntVector rearrangeTemplate(Class shuffletype, S shuffle) { Objects.requireNonNull(shuffle); return VectorSupport.rearrangeOp( - getClass(), shuffletype, null, int.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2444,7 +2448,7 @@ public abstract class IntVector extends AbstractVector { Objects.requireNonNull(shuffle); m.check(masktype, this); return VectorSupport.rearrangeOp( - getClass(), shuffletype, masktype, int.class, length(), + getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), this, shuffle, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2470,7 +2474,7 @@ public abstract class IntVector extends AbstractVector { VectorMask valid = shuffle.laneIsValid(); IntVector r0 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, int.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v0, s_, m_) -> v0.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); @@ -2478,7 +2482,7 @@ public abstract class IntVector extends AbstractVector { })); IntVector r1 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, int.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), v, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2528,7 +2532,7 @@ public abstract class IntVector extends AbstractVector { IntVector compressTemplate(Class masktype, M m) { m.check(masktype, this); return (IntVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_COMPRESS, getClass(), masktype, - int.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> compressHelper(v1, m1)); } @@ -2547,7 +2551,7 @@ public abstract class IntVector extends AbstractVector { IntVector expandTemplate(Class masktype, M m) { m.check(masktype, this); return (IntVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_EXPAND, getClass(), masktype, - int.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> expandHelper(v1, m1)); } @@ -2562,7 +2566,7 @@ public abstract class IntVector extends AbstractVector { /*package-private*/ @ForceInline final IntVector selectFromTemplate(IntVector v) { - return (IntVector)VectorSupport.selectFromOp(getClass(), null, int.class, + return (IntVector)VectorSupport.selectFromOp(getClass(), null, laneTypeOrdinal(), length(), this, v, null, (v1, v2, _m) -> v2.rearrange(v1.toShuffle())); @@ -2582,7 +2586,7 @@ public abstract class IntVector extends AbstractVector { IntVector selectFromTemplate(IntVector v, Class masktype, M m) { m.check(masktype, this); - return (IntVector)VectorSupport.selectFromOp(getClass(), masktype, int.class, + return (IntVector)VectorSupport.selectFromOp(getClass(), masktype, laneTypeOrdinal(), length(), this, v, m, (v1, v2, _m) -> v2.rearrange(v1.toShuffle(), _m)); @@ -2600,7 +2604,7 @@ public abstract class IntVector extends AbstractVector { /*package-private*/ @ForceInline final IntVector selectFromTemplate(IntVector v1, IntVector v2) { - return VectorSupport.selectFromTwoVectorOp(getClass(), int.class, length(), this, v1, v2, + return VectorSupport.selectFromTwoVectorOp(getClass(), laneTypeOrdinal(), length(), this, v1, v2, (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); } @@ -2820,7 +2824,7 @@ public abstract class IntVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), maskClass, int.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, REDUCE_IMPL.find(op, opc, IntVector::reductionOperations))); } @@ -2838,7 +2842,7 @@ public abstract class IntVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), null, int.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, REDUCE_IMPL.find(op, opc, IntVector::reductionOperations))); } @@ -3100,7 +3104,7 @@ public abstract class IntVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, null, int.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, null, a, offset, indexMap, mapOffset, vsp, @@ -3283,7 +3287,7 @@ public abstract class IntVector extends AbstractVector { offset = checkFromIndexSize(offset, length(), a.length); IntSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, @@ -3372,7 +3376,7 @@ public abstract class IntVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), null, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, null, @@ -3499,7 +3503,7 @@ public abstract class IntVector extends AbstractVector { IntVector fromArray0Template(int[] a, int offset) { IntSpecies vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -3516,7 +3520,7 @@ public abstract class IntVector extends AbstractVector { m.check(species()); IntSpecies vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -3549,7 +3553,7 @@ public abstract class IntVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, maskClass, int.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, m, a, offset, indexMap, mapOffset, vsp, @@ -3566,7 +3570,7 @@ public abstract class IntVector extends AbstractVector { IntVector fromMemorySegment0Template(MemorySegment ms, long offset) { IntSpecies vsp = vspecies(); return ScopedMemoryAccess.loadFromMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, vsp, (msp, off, s) -> { return s.ldLongOp((MemorySegment) msp, off, IntVector::memorySegmentGet); @@ -3582,7 +3586,7 @@ public abstract class IntVector extends AbstractVector { IntSpecies vsp = vspecies(); m.check(vsp); return ScopedMemoryAccess.loadFromMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, m, vsp, offsetInRange, (msp, off, s, vm) -> { return s.ldLongOp((MemorySegment) msp, off, vm, IntVector::memorySegmentGet); @@ -3600,7 +3604,7 @@ public abstract class IntVector extends AbstractVector { void intoArray0Template(int[] a, int offset) { IntSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, (arr, off, v) @@ -3617,7 +3621,7 @@ public abstract class IntVector extends AbstractVector { m.check(species()); IntSpecies vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -3646,7 +3650,7 @@ public abstract class IntVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, m, @@ -3665,7 +3669,7 @@ public abstract class IntVector extends AbstractVector { void intoMemorySegment0(MemorySegment ms, long offset) { IntSpecies vsp = vspecies(); ScopedMemoryAccess.storeIntoMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), this, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v) -> { @@ -3682,7 +3686,7 @@ public abstract class IntVector extends AbstractVector { IntSpecies vsp = vspecies(); m.check(vsp); ScopedMemoryAccess.storeIntoMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), this, m, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v, vm) -> { @@ -3898,7 +3902,7 @@ public abstract class IntVector extends AbstractVector { final IntVector broadcastBits(long bits) { return (IntVector) VectorSupport.fromBitsCoerced( - vectorType, int.class, laneCount, + vectorType, laneTypeOrdinal(), laneCount, bits, MODE_BROADCAST, this, (bits_, s_) -> s_.rvOp(i -> bits_)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java index 53fa773555f..59844eabb57 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LaneType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -35,20 +35,19 @@ import static jdk.incubator.vector.VectorIntrinsics.*; * It caches all sorts of goodies that we can't put on java.lang.Class. */ enum LaneType { - FLOAT(float.class, Float.class, float[].class, 'F', 24, Float.SIZE, T_FLOAT), - DOUBLE(double.class, Double.class, double[].class, 'F', 53, Double.SIZE, T_DOUBLE), - BYTE(byte.class, Byte.class, byte[].class, 'I', -1, Byte.SIZE, T_BYTE), - SHORT(short.class, Short.class, short[].class, 'I', -1, Short.SIZE, T_SHORT), - INT(int.class, Integer.class, int[].class, 'I', -1, Integer.SIZE, T_INT), - LONG(long.class, Long.class, long[].class, 'I', -1, Long.SIZE, T_LONG); + FLOAT(float.class, Float.class, float[].class, 'F', 24, Float.SIZE), + DOUBLE(double.class, Double.class, double[].class, 'F', 53, Double.SIZE), + BYTE(byte.class, Byte.class, byte[].class, 'I', -1, Byte.SIZE), + SHORT(short.class, Short.class, short[].class, 'I', -1, Short.SIZE), + INT(int.class, Integer.class, int[].class, 'I', -1, Integer.SIZE), + LONG(long.class, Long.class, long[].class, 'I', -1, Long.SIZE); LaneType(Class elementType, Class genericElementType, Class arrayType, char elementKind, int elementPrecision, - int elementSize, - int basicType) { + int elementSize) { if (elementPrecision <= 0) elementPrecision += elementSize; this.elementType = elementType; @@ -67,12 +66,6 @@ enum LaneType { // report that condition also. this.typeChar = genericElementType.getSimpleName().charAt(0); assert("FDBSIL".indexOf(typeChar) == ordinal()) : this; - // Same as in JVMS, org.objectweb.asm.Opcodes, etc.: - this.basicType = basicType; - assert(basicType == - ( (elementSizeLog2 - /*lg(Byte.SIZE)*/ 3) - | (elementKind == 'F' ? 4 : 8))) : this; - assert("....zcFDBSILoav..".charAt(basicType) == typeChar); } final Class elementType; @@ -85,7 +78,6 @@ enum LaneType { final int switchKey; // 1+ordinal(), which is non-zero final String printName; final char typeChar; // one of "BSILFD" - final int basicType; // lg(size/8) | (kind=='F'?4:kind=='I'?8) private @Stable LaneType asIntegral; private @Stable LaneType asFloating; @@ -194,8 +186,8 @@ enum LaneType { /*package-private*/ @ForceInline - static LaneType ofBasicType(int bt) { - return ENUM_FROM_BT[bt].check(); + static LaneType ofLaneTypeOrdinal(int lo) { + return ENUM_VALUES[lo].check(); } /*package-private*/ @@ -207,13 +199,11 @@ enum LaneType { @Stable private static final LaneType[] ENUM_VALUES; @Stable private static final LaneType[] ENUM_FROM_SK; @Stable private static final LaneType[] ENUM_FROM_C0; - @Stable private static final LaneType[] ENUM_FROM_BT; - private static final int C0_MASK = 0x0F, BT_MASK = 0x0F; + private static final int C0_MASK = 0x0F; static { LaneType[] values = values().clone(); LaneType[] valuesByKey = new LaneType[1+values.length]; LaneType[] valuesByC0 = new LaneType[C0_MASK+1]; - LaneType[] valuesByBT = new LaneType[BT_MASK+1]; for (int ord = 0; ord < values.length; ord++) { int key = 1+ord; LaneType value = values[ord]; @@ -229,8 +219,6 @@ enum LaneType { c0 &= C0_MASK; assert(valuesByC0[c0] == null); valuesByC0[c0] = value; - assert(valuesByBT[value.basicType] == null); - valuesByBT[value.basicType] = value; // set up asIntegral if (value.elementKind == 'I') { value.asIntegral = value; @@ -276,15 +264,14 @@ enum LaneType { ENUM_VALUES = values; ENUM_FROM_SK = valuesByKey; ENUM_FROM_C0 = valuesByC0; - ENUM_FROM_BT = valuesByBT; } static { - assert(ofBasicType(T_FLOAT) == FLOAT); - assert(ofBasicType(T_DOUBLE) == DOUBLE); - assert(ofBasicType(T_BYTE) == BYTE); - assert(ofBasicType(T_SHORT) == SHORT); - assert(ofBasicType(T_INT) == INT); - assert(ofBasicType(T_LONG) == LONG); + assert(ofLaneTypeOrdinal(LT_FLOAT) == FLOAT); + assert(ofLaneTypeOrdinal(LT_DOUBLE) == DOUBLE); + assert(ofLaneTypeOrdinal(LT_BYTE) == BYTE); + assert(ofLaneTypeOrdinal(LT_SHORT) == SHORT); + assert(ofLaneTypeOrdinal(LT_INT) == INT); + assert(ofLaneTypeOrdinal(LT_LONG) == LONG); } } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java index 5a47153837a..01d721f64fc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long128Vector.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 @@ -119,6 +119,13 @@ final class Long128Vector extends LongVector { return (long[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -517,7 +524,7 @@ final class Long128Vector extends LongVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { long[] vecarr = vec.vec(); @@ -538,7 +545,7 @@ final class Long128Vector extends LongVector { @ForceInline public Long128Vector withLaneHelper(int i, long e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { long[] res = v.vec().clone(); @@ -642,8 +649,8 @@ final class Long128Vector extends LongVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -653,7 +660,7 @@ final class Long128Vector extends LongVector { /*package-private*/ Long128Mask indexPartiallyInUpperRange(long offset, long limit) { return (Long128Mask) VectorSupport.indexPartiallyInUpperRange( - Long128Mask.class, long.class, VLENGTH, offset, limit, + Long128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Long128Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -669,7 +676,7 @@ final class Long128Vector extends LongVector { @ForceInline public Long128Mask compress() { return (Long128Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Long128Vector.class, Long128Mask.class, ETYPE, VLENGTH, null, this, + Long128Vector.class, Long128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -681,7 +688,7 @@ final class Long128Vector extends LongVector { public Long128Mask and(VectorMask mask) { Objects.requireNonNull(mask); Long128Mask m = (Long128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Long128Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Long128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -691,7 +698,7 @@ final class Long128Vector extends LongVector { public Long128Mask or(VectorMask mask) { Objects.requireNonNull(mask); Long128Mask m = (Long128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Long128Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Long128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -701,7 +708,7 @@ final class Long128Vector extends LongVector { public Long128Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Long128Mask m = (Long128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Long128Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Long128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -711,21 +718,21 @@ final class Long128Vector extends LongVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Long128Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Long128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Long128Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Long128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Long128Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Long128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -735,7 +742,7 @@ final class Long128Vector extends LongVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Long128Mask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Long128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -745,7 +752,7 @@ final class Long128Vector extends LongVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Long128Mask.class, long.class, VLENGTH, + return VectorSupport.extract(Long128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -754,7 +761,7 @@ final class Long128Vector extends LongVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Long128Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, Long128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Long128Mask)m).getBits())); } @@ -762,7 +769,7 @@ final class Long128Vector extends LongVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Long128Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, Long128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Long128Mask)m).getBits())); } @@ -770,7 +777,7 @@ final class Long128Vector extends LongVector { @ForceInline /*package-private*/ static Long128Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Long128Mask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Long128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java index 9c3ff7627cc..b3e7022771c 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long256Vector.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 @@ -119,6 +119,13 @@ final class Long256Vector extends LongVector { return (long[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -519,7 +526,7 @@ final class Long256Vector extends LongVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { long[] vecarr = vec.vec(); @@ -542,7 +549,7 @@ final class Long256Vector extends LongVector { @ForceInline public Long256Vector withLaneHelper(int i, long e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { long[] res = v.vec().clone(); @@ -646,8 +653,8 @@ final class Long256Vector extends LongVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -657,7 +664,7 @@ final class Long256Vector extends LongVector { /*package-private*/ Long256Mask indexPartiallyInUpperRange(long offset, long limit) { return (Long256Mask) VectorSupport.indexPartiallyInUpperRange( - Long256Mask.class, long.class, VLENGTH, offset, limit, + Long256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Long256Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -673,7 +680,7 @@ final class Long256Vector extends LongVector { @ForceInline public Long256Mask compress() { return (Long256Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Long256Vector.class, Long256Mask.class, ETYPE, VLENGTH, null, this, + Long256Vector.class, Long256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -685,7 +692,7 @@ final class Long256Vector extends LongVector { public Long256Mask and(VectorMask mask) { Objects.requireNonNull(mask); Long256Mask m = (Long256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Long256Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Long256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -695,7 +702,7 @@ final class Long256Vector extends LongVector { public Long256Mask or(VectorMask mask) { Objects.requireNonNull(mask); Long256Mask m = (Long256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Long256Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Long256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -705,7 +712,7 @@ final class Long256Vector extends LongVector { public Long256Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Long256Mask m = (Long256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Long256Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Long256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -715,21 +722,21 @@ final class Long256Vector extends LongVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Long256Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Long256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Long256Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Long256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Long256Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Long256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -739,7 +746,7 @@ final class Long256Vector extends LongVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Long256Mask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Long256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -749,7 +756,7 @@ final class Long256Vector extends LongVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Long256Mask.class, long.class, VLENGTH, + return VectorSupport.extract(Long256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -758,7 +765,7 @@ final class Long256Vector extends LongVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Long256Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, Long256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Long256Mask)m).getBits())); } @@ -766,7 +773,7 @@ final class Long256Vector extends LongVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Long256Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, Long256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Long256Mask)m).getBits())); } @@ -774,7 +781,7 @@ final class Long256Vector extends LongVector { @ForceInline /*package-private*/ static Long256Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Long256Mask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Long256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java index 30d29c3cd03..169664bc242 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long512Vector.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 @@ -119,6 +119,13 @@ final class Long512Vector extends LongVector { return (long[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -523,7 +530,7 @@ final class Long512Vector extends LongVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { long[] vecarr = vec.vec(); @@ -550,7 +557,7 @@ final class Long512Vector extends LongVector { @ForceInline public Long512Vector withLaneHelper(int i, long e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { long[] res = v.vec().clone(); @@ -654,8 +661,8 @@ final class Long512Vector extends LongVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -665,7 +672,7 @@ final class Long512Vector extends LongVector { /*package-private*/ Long512Mask indexPartiallyInUpperRange(long offset, long limit) { return (Long512Mask) VectorSupport.indexPartiallyInUpperRange( - Long512Mask.class, long.class, VLENGTH, offset, limit, + Long512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Long512Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -681,7 +688,7 @@ final class Long512Vector extends LongVector { @ForceInline public Long512Mask compress() { return (Long512Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Long512Vector.class, Long512Mask.class, ETYPE, VLENGTH, null, this, + Long512Vector.class, Long512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -693,7 +700,7 @@ final class Long512Vector extends LongVector { public Long512Mask and(VectorMask mask) { Objects.requireNonNull(mask); Long512Mask m = (Long512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Long512Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Long512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -703,7 +710,7 @@ final class Long512Vector extends LongVector { public Long512Mask or(VectorMask mask) { Objects.requireNonNull(mask); Long512Mask m = (Long512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Long512Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Long512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -713,7 +720,7 @@ final class Long512Vector extends LongVector { public Long512Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Long512Mask m = (Long512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Long512Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Long512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -723,21 +730,21 @@ final class Long512Vector extends LongVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Long512Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Long512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Long512Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Long512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Long512Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Long512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -747,7 +754,7 @@ final class Long512Vector extends LongVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Long512Mask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Long512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -757,7 +764,7 @@ final class Long512Vector extends LongVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Long512Mask.class, long.class, VLENGTH, + return VectorSupport.extract(Long512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -766,7 +773,7 @@ final class Long512Vector extends LongVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Long512Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, Long512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Long512Mask)m).getBits())); } @@ -774,7 +781,7 @@ final class Long512Vector extends LongVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Long512Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, Long512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Long512Mask)m).getBits())); } @@ -782,7 +789,7 @@ final class Long512Vector extends LongVector { @ForceInline /*package-private*/ static Long512Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Long512Mask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Long512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java index 518baa168ec..66c84fde56f 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Long64Vector.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 @@ -119,6 +119,13 @@ final class Long64Vector extends LongVector { return (long[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -516,7 +523,7 @@ final class Long64Vector extends LongVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { long[] vecarr = vec.vec(); @@ -536,7 +543,7 @@ final class Long64Vector extends LongVector { @ForceInline public Long64Vector withLaneHelper(int i, long e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { long[] res = v.vec().clone(); @@ -640,8 +647,8 @@ final class Long64Vector extends LongVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -651,7 +658,7 @@ final class Long64Vector extends LongVector { /*package-private*/ Long64Mask indexPartiallyInUpperRange(long offset, long limit) { return (Long64Mask) VectorSupport.indexPartiallyInUpperRange( - Long64Mask.class, long.class, VLENGTH, offset, limit, + Long64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Long64Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -667,7 +674,7 @@ final class Long64Vector extends LongVector { @ForceInline public Long64Mask compress() { return (Long64Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Long64Vector.class, Long64Mask.class, ETYPE, VLENGTH, null, this, + Long64Vector.class, Long64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -679,7 +686,7 @@ final class Long64Vector extends LongVector { public Long64Mask and(VectorMask mask) { Objects.requireNonNull(mask); Long64Mask m = (Long64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Long64Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Long64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -689,7 +696,7 @@ final class Long64Vector extends LongVector { public Long64Mask or(VectorMask mask) { Objects.requireNonNull(mask); Long64Mask m = (Long64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Long64Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Long64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -699,7 +706,7 @@ final class Long64Vector extends LongVector { public Long64Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Long64Mask m = (Long64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Long64Mask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Long64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -709,21 +716,21 @@ final class Long64Vector extends LongVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Long64Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Long64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Long64Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Long64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Long64Mask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Long64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -733,7 +740,7 @@ final class Long64Vector extends LongVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Long64Mask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Long64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -743,7 +750,7 @@ final class Long64Vector extends LongVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Long64Mask.class, long.class, VLENGTH, + return VectorSupport.extract(Long64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -752,7 +759,7 @@ final class Long64Vector extends LongVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Long64Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, Long64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Long64Mask)m).getBits())); } @@ -760,7 +767,7 @@ final class Long64Vector extends LongVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Long64Mask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, Long64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Long64Mask)m).getBits())); } @@ -768,7 +775,7 @@ final class Long64Vector extends LongVector { @ForceInline /*package-private*/ static Long64Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Long64Mask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Long64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java index 3d981c37ae2..c95db0c4482 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongMaxVector.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 @@ -119,6 +119,13 @@ final class LongMaxVector extends LongVector { return (long[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -516,7 +523,7 @@ final class LongMaxVector extends LongVector { @ForceInline public long laneHelper(int i) { return (long) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { long[] vecarr = vec.vec(); @@ -536,7 +543,7 @@ final class LongMaxVector extends LongVector { @ForceInline public LongMaxVector withLaneHelper(int i, long e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { long[] res = v.vec().clone(); @@ -640,8 +647,8 @@ final class LongMaxVector extends LongVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -651,7 +658,7 @@ final class LongMaxVector extends LongVector { /*package-private*/ LongMaxMask indexPartiallyInUpperRange(long offset, long limit) { return (LongMaxMask) VectorSupport.indexPartiallyInUpperRange( - LongMaxMask.class, long.class, VLENGTH, offset, limit, + LongMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (LongMaxMask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -667,7 +674,7 @@ final class LongMaxVector extends LongVector { @ForceInline public LongMaxMask compress() { return (LongMaxMask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - LongMaxVector.class, LongMaxMask.class, ETYPE, VLENGTH, null, this, + LongMaxVector.class, LongMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -679,7 +686,7 @@ final class LongMaxVector extends LongVector { public LongMaxMask and(VectorMask mask) { Objects.requireNonNull(mask); LongMaxMask m = (LongMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, LongMaxMask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, LongMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -689,7 +696,7 @@ final class LongMaxVector extends LongVector { public LongMaxMask or(VectorMask mask) { Objects.requireNonNull(mask); LongMaxMask m = (LongMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, LongMaxMask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, LongMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -699,7 +706,7 @@ final class LongMaxVector extends LongVector { public LongMaxMask xor(VectorMask mask) { Objects.requireNonNull(mask); LongMaxMask m = (LongMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, LongMaxMask.class, null, long.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, LongMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -709,21 +716,21 @@ final class LongMaxVector extends LongVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, LongMaxMask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, LongMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, LongMaxMask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, LongMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, LongMaxMask.class, long.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, LongMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -733,7 +740,7 @@ final class LongMaxVector extends LongVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, LongMaxMask.class, long.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, LongMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -743,7 +750,7 @@ final class LongMaxVector extends LongVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(LongMaxMask.class, long.class, VLENGTH, + return VectorSupport.extract(LongMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -752,7 +759,7 @@ final class LongMaxVector extends LongVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, LongMaxMask.class, long.class, VLENGTH, + return VectorSupport.test(BT_ne, LongMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((LongMaxMask)m).getBits())); } @@ -760,7 +767,7 @@ final class LongMaxVector extends LongVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, LongMaxMask.class, long.class, VLENGTH, + return VectorSupport.test(BT_overflow, LongMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((LongMaxMask)m).getBits())); } @@ -768,7 +775,7 @@ final class LongMaxVector extends LongVector { @ForceInline /*package-private*/ static LongMaxMask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(LongMaxMask.class, long.class, VLENGTH, + return VectorSupport.fromBitsCoerced(LongMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java index b842bdebdc4..5657bbec0a6 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/LongVector.java @@ -59,6 +59,10 @@ public abstract class LongVector extends AbstractVector { static final ValueLayout.OfLong ELEMENT_LAYOUT = ValueLayout.JAVA_LONG.withByteAlignment(1); + static final int LANE_TYPE_ORDINAL = LT_LONG; + + static final int LANEBITS_TYPE_ORDINAL = LT_LONG; + @ForceInline static int opCode(Operator op) { return VectorOperators.opCode(op, VO_OPCODE_VALID, FORBID_OPCODE_KIND); @@ -573,7 +577,7 @@ public abstract class LongVector extends AbstractVector { @ForceInline public static LongVector zero(VectorSpecies species) { LongSpecies vsp = (LongSpecies) species; - return VectorSupport.fromBitsCoerced(vsp.vectorType(), long.class, species.length(), + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), 0, MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); } @@ -653,7 +657,7 @@ public abstract class LongVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), null, long.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, UN_IMPL.find(op, opc, LongVector::unaryOperations)); } @@ -681,7 +685,7 @@ public abstract class LongVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), maskClass, long.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, UN_IMPL.find(op, opc, LongVector::unaryOperations)); } @@ -754,7 +758,7 @@ public abstract class LongVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), null, long.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, null, BIN_IMPL.find(op, opc, LongVector::binaryOperations)); } @@ -805,7 +809,7 @@ public abstract class LongVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), maskClass, long.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, m, BIN_IMPL.find(op, opc, LongVector::binaryOperations)); } @@ -951,7 +955,7 @@ public abstract class LongVector extends AbstractVector { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), null, long.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, e, null, BIN_INT_IMPL.find(op, opc, LongVector::broadcastIntOperations)); } @@ -972,7 +976,7 @@ public abstract class LongVector extends AbstractVector { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), maskClass, long.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, e, m, BIN_INT_IMPL.find(op, opc, LongVector::broadcastIntOperations)); } @@ -1048,7 +1052,7 @@ public abstract class LongVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), null, long.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, tother, null, TERN_IMPL.find(op, opc, LongVector::ternaryOperations)); } @@ -1088,7 +1092,7 @@ public abstract class LongVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), maskClass, long.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, tother, m, TERN_IMPL.find(op, opc, LongVector::ternaryOperations)); } @@ -1968,7 +1972,7 @@ public abstract class LongVector extends AbstractVector { that.check(this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, long.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, null, (cond, v0, v1, m1) -> { AbstractMask m @@ -1990,7 +1994,7 @@ public abstract class LongVector extends AbstractVector { m.check(maskType, this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, long.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, m, (cond, v0, v1, m1) -> { AbstractMask cmpM @@ -2095,7 +2099,7 @@ public abstract class LongVector extends AbstractVector { blendTemplate(Class maskType, LongVector v, M m) { v.check(this); return VectorSupport.blend( - getClass(), maskType, long.class, length(), + getClass(), maskType, laneTypeOrdinal(), length(), this, v, m, (v0, v1, m_) -> v0.bOp(v1, m_, (i, a, b) -> b)); } @@ -2112,7 +2116,7 @@ public abstract class LongVector extends AbstractVector { // make sure VLENGTH*scale doesn't overflow: vsp.checkScale(scale); return VectorSupport.indexVector( - getClass(), long.class, length(), + getClass(), laneTypeOrdinal(), length(), this, scale, vsp, (v, scale_, s) -> { @@ -2283,7 +2287,7 @@ public abstract class LongVector extends AbstractVector { LongVector rearrangeTemplate(Class shuffletype, S shuffle) { Objects.requireNonNull(shuffle); return VectorSupport.rearrangeOp( - getClass(), shuffletype, null, long.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2310,7 +2314,7 @@ public abstract class LongVector extends AbstractVector { Objects.requireNonNull(shuffle); m.check(masktype, this); return VectorSupport.rearrangeOp( - getClass(), shuffletype, masktype, long.class, length(), + getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), this, shuffle, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2336,7 +2340,7 @@ public abstract class LongVector extends AbstractVector { VectorMask valid = shuffle.laneIsValid(); LongVector r0 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, long.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v0, s_, m_) -> v0.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); @@ -2344,7 +2348,7 @@ public abstract class LongVector extends AbstractVector { })); LongVector r1 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, long.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), v, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2394,7 +2398,7 @@ public abstract class LongVector extends AbstractVector { LongVector compressTemplate(Class masktype, M m) { m.check(masktype, this); return (LongVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_COMPRESS, getClass(), masktype, - long.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> compressHelper(v1, m1)); } @@ -2413,7 +2417,7 @@ public abstract class LongVector extends AbstractVector { LongVector expandTemplate(Class masktype, M m) { m.check(masktype, this); return (LongVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_EXPAND, getClass(), masktype, - long.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> expandHelper(v1, m1)); } @@ -2428,7 +2432,7 @@ public abstract class LongVector extends AbstractVector { /*package-private*/ @ForceInline final LongVector selectFromTemplate(LongVector v) { - return (LongVector)VectorSupport.selectFromOp(getClass(), null, long.class, + return (LongVector)VectorSupport.selectFromOp(getClass(), null, laneTypeOrdinal(), length(), this, v, null, (v1, v2, _m) -> v2.rearrange(v1.toShuffle())); @@ -2448,7 +2452,7 @@ public abstract class LongVector extends AbstractVector { LongVector selectFromTemplate(LongVector v, Class masktype, M m) { m.check(masktype, this); - return (LongVector)VectorSupport.selectFromOp(getClass(), masktype, long.class, + return (LongVector)VectorSupport.selectFromOp(getClass(), masktype, laneTypeOrdinal(), length(), this, v, m, (v1, v2, _m) -> v2.rearrange(v1.toShuffle(), _m)); @@ -2466,7 +2470,7 @@ public abstract class LongVector extends AbstractVector { /*package-private*/ @ForceInline final LongVector selectFromTemplate(LongVector v1, LongVector v2) { - return VectorSupport.selectFromTwoVectorOp(getClass(), long.class, length(), this, v1, v2, + return VectorSupport.selectFromTwoVectorOp(getClass(), laneTypeOrdinal(), length(), this, v1, v2, (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); } @@ -2686,7 +2690,7 @@ public abstract class LongVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), maskClass, long.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, REDUCE_IMPL.find(op, opc, LongVector::reductionOperations))); } @@ -2704,7 +2708,7 @@ public abstract class LongVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), null, long.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, REDUCE_IMPL.find(op, opc, LongVector::reductionOperations))); } @@ -2979,7 +2983,7 @@ public abstract class LongVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, null, long.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, null, a, offset, indexMap, mapOffset, vsp, @@ -3162,7 +3166,7 @@ public abstract class LongVector extends AbstractVector { offset = checkFromIndexSize(offset, length(), a.length); LongSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, @@ -3270,7 +3274,7 @@ public abstract class LongVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), null, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, null, @@ -3397,7 +3401,7 @@ public abstract class LongVector extends AbstractVector { LongVector fromArray0Template(long[] a, int offset) { LongSpecies vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -3414,7 +3418,7 @@ public abstract class LongVector extends AbstractVector { m.check(species()); LongSpecies vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -3465,7 +3469,7 @@ public abstract class LongVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, maskClass, long.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, m, a, offset, indexMap, mapOffset, vsp, @@ -3482,7 +3486,7 @@ public abstract class LongVector extends AbstractVector { LongVector fromMemorySegment0Template(MemorySegment ms, long offset) { LongSpecies vsp = vspecies(); return ScopedMemoryAccess.loadFromMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, vsp, (msp, off, s) -> { return s.ldLongOp((MemorySegment) msp, off, LongVector::memorySegmentGet); @@ -3498,7 +3502,7 @@ public abstract class LongVector extends AbstractVector { LongSpecies vsp = vspecies(); m.check(vsp); return ScopedMemoryAccess.loadFromMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, m, vsp, offsetInRange, (msp, off, s, vm) -> { return s.ldLongOp((MemorySegment) msp, off, vm, LongVector::memorySegmentGet); @@ -3516,7 +3520,7 @@ public abstract class LongVector extends AbstractVector { void intoArray0Template(long[] a, int offset) { LongSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, (arr, off, v) @@ -3533,7 +3537,7 @@ public abstract class LongVector extends AbstractVector { m.check(species()); LongSpecies vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -3581,7 +3585,7 @@ public abstract class LongVector extends AbstractVector { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, m, @@ -3600,7 +3604,7 @@ public abstract class LongVector extends AbstractVector { void intoMemorySegment0(MemorySegment ms, long offset) { LongSpecies vsp = vspecies(); ScopedMemoryAccess.storeIntoMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), this, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v) -> { @@ -3617,7 +3621,7 @@ public abstract class LongVector extends AbstractVector { LongSpecies vsp = vspecies(); m.check(vsp); ScopedMemoryAccess.storeIntoMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), this, m, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v, vm) -> { @@ -3833,7 +3837,7 @@ public abstract class LongVector extends AbstractVector { final LongVector broadcastBits(long bits) { return (LongVector) VectorSupport.fromBitsCoerced( - vectorType, long.class, laneCount, + vectorType, laneTypeOrdinal(), laneCount, bits, MODE_BROADCAST, this, (bits_, s_) -> s_.rvOp(i -> bits_)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java index 60f22eb5fb9..cc35a1ea069 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short128Vector.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 @@ -119,6 +119,13 @@ final class Short128Vector extends ShortVector { return (short[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -533,7 +540,7 @@ final class Short128Vector extends ShortVector { @ForceInline public short laneHelper(int i) { return (short) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { short[] vecarr = vec.vec(); @@ -560,7 +567,7 @@ final class Short128Vector extends ShortVector { @ForceInline public Short128Vector withLaneHelper(int i, short e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { short[] res = v.vec().clone(); @@ -664,8 +671,8 @@ final class Short128Vector extends ShortVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -675,7 +682,7 @@ final class Short128Vector extends ShortVector { /*package-private*/ Short128Mask indexPartiallyInUpperRange(long offset, long limit) { return (Short128Mask) VectorSupport.indexPartiallyInUpperRange( - Short128Mask.class, short.class, VLENGTH, offset, limit, + Short128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Short128Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -691,7 +698,7 @@ final class Short128Vector extends ShortVector { @ForceInline public Short128Mask compress() { return (Short128Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Short128Vector.class, Short128Mask.class, ETYPE, VLENGTH, null, this, + Short128Vector.class, Short128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -703,7 +710,7 @@ final class Short128Vector extends ShortVector { public Short128Mask and(VectorMask mask) { Objects.requireNonNull(mask); Short128Mask m = (Short128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Short128Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Short128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -713,7 +720,7 @@ final class Short128Vector extends ShortVector { public Short128Mask or(VectorMask mask) { Objects.requireNonNull(mask); Short128Mask m = (Short128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Short128Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Short128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -723,7 +730,7 @@ final class Short128Vector extends ShortVector { public Short128Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Short128Mask m = (Short128Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Short128Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Short128Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -733,21 +740,21 @@ final class Short128Vector extends ShortVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Short128Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Short128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Short128Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Short128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Short128Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Short128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -757,7 +764,7 @@ final class Short128Vector extends ShortVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Short128Mask.class, short.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Short128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -767,7 +774,7 @@ final class Short128Vector extends ShortVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Short128Mask.class, short.class, VLENGTH, + return VectorSupport.extract(Short128Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -776,7 +783,7 @@ final class Short128Vector extends ShortVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Short128Mask.class, short.class, VLENGTH, + return VectorSupport.test(BT_ne, Short128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Short128Mask)m).getBits())); } @@ -784,7 +791,7 @@ final class Short128Vector extends ShortVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Short128Mask.class, short.class, VLENGTH, + return VectorSupport.test(BT_overflow, Short128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Short128Mask)m).getBits())); } @@ -792,7 +799,7 @@ final class Short128Vector extends ShortVector { @ForceInline /*package-private*/ static Short128Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Short128Mask.class, short.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Short128Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java index 14c415afac2..a3841eb63dc 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short256Vector.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 @@ -119,6 +119,13 @@ final class Short256Vector extends ShortVector { return (short[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -541,7 +548,7 @@ final class Short256Vector extends ShortVector { @ForceInline public short laneHelper(int i) { return (short) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { short[] vecarr = vec.vec(); @@ -576,7 +583,7 @@ final class Short256Vector extends ShortVector { @ForceInline public Short256Vector withLaneHelper(int i, short e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { short[] res = v.vec().clone(); @@ -680,8 +687,8 @@ final class Short256Vector extends ShortVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -691,7 +698,7 @@ final class Short256Vector extends ShortVector { /*package-private*/ Short256Mask indexPartiallyInUpperRange(long offset, long limit) { return (Short256Mask) VectorSupport.indexPartiallyInUpperRange( - Short256Mask.class, short.class, VLENGTH, offset, limit, + Short256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Short256Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -707,7 +714,7 @@ final class Short256Vector extends ShortVector { @ForceInline public Short256Mask compress() { return (Short256Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Short256Vector.class, Short256Mask.class, ETYPE, VLENGTH, null, this, + Short256Vector.class, Short256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -719,7 +726,7 @@ final class Short256Vector extends ShortVector { public Short256Mask and(VectorMask mask) { Objects.requireNonNull(mask); Short256Mask m = (Short256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Short256Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Short256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -729,7 +736,7 @@ final class Short256Vector extends ShortVector { public Short256Mask or(VectorMask mask) { Objects.requireNonNull(mask); Short256Mask m = (Short256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Short256Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Short256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -739,7 +746,7 @@ final class Short256Vector extends ShortVector { public Short256Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Short256Mask m = (Short256Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Short256Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Short256Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -749,21 +756,21 @@ final class Short256Vector extends ShortVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Short256Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Short256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Short256Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Short256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Short256Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Short256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -773,7 +780,7 @@ final class Short256Vector extends ShortVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Short256Mask.class, short.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Short256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -783,7 +790,7 @@ final class Short256Vector extends ShortVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Short256Mask.class, short.class, VLENGTH, + return VectorSupport.extract(Short256Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -792,7 +799,7 @@ final class Short256Vector extends ShortVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Short256Mask.class, short.class, VLENGTH, + return VectorSupport.test(BT_ne, Short256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Short256Mask)m).getBits())); } @@ -800,7 +807,7 @@ final class Short256Vector extends ShortVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Short256Mask.class, short.class, VLENGTH, + return VectorSupport.test(BT_overflow, Short256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Short256Mask)m).getBits())); } @@ -808,7 +815,7 @@ final class Short256Vector extends ShortVector { @ForceInline /*package-private*/ static Short256Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Short256Mask.class, short.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Short256Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java index c92979302a6..3da20257a8d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short512Vector.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 @@ -119,6 +119,13 @@ final class Short512Vector extends ShortVector { return (short[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -557,7 +564,7 @@ final class Short512Vector extends ShortVector { @ForceInline public short laneHelper(int i) { return (short) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { short[] vecarr = vec.vec(); @@ -608,7 +615,7 @@ final class Short512Vector extends ShortVector { @ForceInline public Short512Vector withLaneHelper(int i, short e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { short[] res = v.vec().clone(); @@ -712,8 +719,8 @@ final class Short512Vector extends ShortVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -723,7 +730,7 @@ final class Short512Vector extends ShortVector { /*package-private*/ Short512Mask indexPartiallyInUpperRange(long offset, long limit) { return (Short512Mask) VectorSupport.indexPartiallyInUpperRange( - Short512Mask.class, short.class, VLENGTH, offset, limit, + Short512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Short512Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -739,7 +746,7 @@ final class Short512Vector extends ShortVector { @ForceInline public Short512Mask compress() { return (Short512Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Short512Vector.class, Short512Mask.class, ETYPE, VLENGTH, null, this, + Short512Vector.class, Short512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -751,7 +758,7 @@ final class Short512Vector extends ShortVector { public Short512Mask and(VectorMask mask) { Objects.requireNonNull(mask); Short512Mask m = (Short512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Short512Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Short512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -761,7 +768,7 @@ final class Short512Vector extends ShortVector { public Short512Mask or(VectorMask mask) { Objects.requireNonNull(mask); Short512Mask m = (Short512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Short512Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Short512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -771,7 +778,7 @@ final class Short512Vector extends ShortVector { public Short512Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Short512Mask m = (Short512Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Short512Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Short512Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -781,21 +788,21 @@ final class Short512Vector extends ShortVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Short512Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Short512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Short512Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Short512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Short512Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Short512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -805,7 +812,7 @@ final class Short512Vector extends ShortVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Short512Mask.class, short.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Short512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -815,7 +822,7 @@ final class Short512Vector extends ShortVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Short512Mask.class, short.class, VLENGTH, + return VectorSupport.extract(Short512Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -824,7 +831,7 @@ final class Short512Vector extends ShortVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Short512Mask.class, short.class, VLENGTH, + return VectorSupport.test(BT_ne, Short512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Short512Mask)m).getBits())); } @@ -832,7 +839,7 @@ final class Short512Vector extends ShortVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Short512Mask.class, short.class, VLENGTH, + return VectorSupport.test(BT_overflow, Short512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Short512Mask)m).getBits())); } @@ -840,7 +847,7 @@ final class Short512Vector extends ShortVector { @ForceInline /*package-private*/ static Short512Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Short512Mask.class, short.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Short512Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java index b454989b45f..8b3246996f3 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Short64Vector.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 @@ -119,6 +119,13 @@ final class Short64Vector extends ShortVector { return (short[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -529,7 +536,7 @@ final class Short64Vector extends ShortVector { @ForceInline public short laneHelper(int i) { return (short) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { short[] vecarr = vec.vec(); @@ -552,7 +559,7 @@ final class Short64Vector extends ShortVector { @ForceInline public Short64Vector withLaneHelper(int i, short e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { short[] res = v.vec().clone(); @@ -656,8 +663,8 @@ final class Short64Vector extends ShortVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -667,7 +674,7 @@ final class Short64Vector extends ShortVector { /*package-private*/ Short64Mask indexPartiallyInUpperRange(long offset, long limit) { return (Short64Mask) VectorSupport.indexPartiallyInUpperRange( - Short64Mask.class, short.class, VLENGTH, offset, limit, + Short64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (Short64Mask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -683,7 +690,7 @@ final class Short64Vector extends ShortVector { @ForceInline public Short64Mask compress() { return (Short64Mask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - Short64Vector.class, Short64Mask.class, ETYPE, VLENGTH, null, this, + Short64Vector.class, Short64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -695,7 +702,7 @@ final class Short64Vector extends ShortVector { public Short64Mask and(VectorMask mask) { Objects.requireNonNull(mask); Short64Mask m = (Short64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, Short64Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, Short64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -705,7 +712,7 @@ final class Short64Vector extends ShortVector { public Short64Mask or(VectorMask mask) { Objects.requireNonNull(mask); Short64Mask m = (Short64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, Short64Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, Short64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -715,7 +722,7 @@ final class Short64Vector extends ShortVector { public Short64Mask xor(VectorMask mask) { Objects.requireNonNull(mask); Short64Mask m = (Short64Mask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, Short64Mask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, Short64Mask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -725,21 +732,21 @@ final class Short64Vector extends ShortVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Short64Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, Short64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Short64Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, Short64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Short64Mask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, Short64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -749,7 +756,7 @@ final class Short64Vector extends ShortVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Short64Mask.class, short.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, Short64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -759,7 +766,7 @@ final class Short64Vector extends ShortVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(Short64Mask.class, short.class, VLENGTH, + return VectorSupport.extract(Short64Mask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -768,7 +775,7 @@ final class Short64Vector extends ShortVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, Short64Mask.class, short.class, VLENGTH, + return VectorSupport.test(BT_ne, Short64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((Short64Mask)m).getBits())); } @@ -776,7 +783,7 @@ final class Short64Vector extends ShortVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, Short64Mask.class, short.class, VLENGTH, + return VectorSupport.test(BT_overflow, Short64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((Short64Mask)m).getBits())); } @@ -784,7 +791,7 @@ final class Short64Vector extends ShortVector { @ForceInline /*package-private*/ static Short64Mask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(Short64Mask.class, short.class, VLENGTH, + return VectorSupport.fromBitsCoerced(Short64Mask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java index bea14b7d5fd..bf9b13c6606 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortMaxVector.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 @@ -119,6 +119,13 @@ final class ShortMaxVector extends ShortVector { return (short[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -526,7 +533,7 @@ final class ShortMaxVector extends ShortVector { @ForceInline public short laneHelper(int i) { return (short) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { short[] vecarr = vec.vec(); @@ -546,7 +553,7 @@ final class ShortMaxVector extends ShortVector { @ForceInline public ShortMaxVector withLaneHelper(int i, short e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { short[] res = v.vec().clone(); @@ -650,8 +657,8 @@ final class ShortMaxVector extends ShortVector { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -661,7 +668,7 @@ final class ShortMaxVector extends ShortVector { /*package-private*/ ShortMaxMask indexPartiallyInUpperRange(long offset, long limit) { return (ShortMaxMask) VectorSupport.indexPartiallyInUpperRange( - ShortMaxMask.class, short.class, VLENGTH, offset, limit, + ShortMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> (ShortMaxMask) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -677,7 +684,7 @@ final class ShortMaxVector extends ShortVector { @ForceInline public ShortMaxMask compress() { return (ShortMaxMask)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - ShortMaxVector.class, ShortMaxMask.class, ETYPE, VLENGTH, null, this, + ShortMaxVector.class, ShortMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -689,7 +696,7 @@ final class ShortMaxVector extends ShortVector { public ShortMaxMask and(VectorMask mask) { Objects.requireNonNull(mask); ShortMaxMask m = (ShortMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, ShortMaxMask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, ShortMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -699,7 +706,7 @@ final class ShortMaxVector extends ShortVector { public ShortMaxMask or(VectorMask mask) { Objects.requireNonNull(mask); ShortMaxMask m = (ShortMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, ShortMaxMask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, ShortMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -709,7 +716,7 @@ final class ShortMaxVector extends ShortVector { public ShortMaxMask xor(VectorMask mask) { Objects.requireNonNull(mask); ShortMaxMask m = (ShortMaxMask)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, ShortMaxMask.class, null, short.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, ShortMaxMask.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -719,21 +726,21 @@ final class ShortMaxVector extends ShortVector { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, ShortMaxMask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, ShortMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, ShortMaxMask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, ShortMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, ShortMaxMask.class, short.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, ShortMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -743,7 +750,7 @@ final class ShortMaxVector extends ShortVector { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, ShortMaxMask.class, short.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, ShortMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -753,7 +760,7 @@ final class ShortMaxVector extends ShortVector { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract(ShortMaxMask.class, short.class, VLENGTH, + return VectorSupport.extract(ShortMaxMask.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -762,7 +769,7 @@ final class ShortMaxVector extends ShortVector { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, ShortMaxMask.class, short.class, VLENGTH, + return VectorSupport.test(BT_ne, ShortMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper(((ShortMaxMask)m).getBits())); } @@ -770,7 +777,7 @@ final class ShortMaxVector extends ShortVector { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, ShortMaxMask.class, short.class, VLENGTH, + return VectorSupport.test(BT_overflow, ShortMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper(((ShortMaxMask)m).getBits())); } @@ -778,7 +785,7 @@ final class ShortMaxVector extends ShortVector { @ForceInline /*package-private*/ static ShortMaxMask maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced(ShortMaxMask.class, short.class, VLENGTH, + return VectorSupport.fromBitsCoerced(ShortMaxMask.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java index 8b4c9bc5a77..03b7bb030b9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ShortVector.java @@ -59,6 +59,10 @@ public abstract class ShortVector extends AbstractVector { static final ValueLayout.OfShort ELEMENT_LAYOUT = ValueLayout.JAVA_SHORT.withByteAlignment(1); + static final int LANE_TYPE_ORDINAL = LT_SHORT; + + static final int LANEBITS_TYPE_ORDINAL = LT_SHORT; + @ForceInline static int opCode(Operator op) { return VectorOperators.opCode(op, VO_OPCODE_VALID, FORBID_OPCODE_KIND); @@ -573,7 +577,7 @@ public abstract class ShortVector extends AbstractVector { @ForceInline public static ShortVector zero(VectorSpecies species) { ShortSpecies vsp = (ShortSpecies) species; - return VectorSupport.fromBitsCoerced(vsp.vectorType(), short.class, species.length(), + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), 0, MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); } @@ -695,7 +699,7 @@ public abstract class ShortVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), null, short.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, UN_IMPL.find(op, opc, ShortVector::unaryOperations)); } @@ -723,7 +727,7 @@ public abstract class ShortVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), maskClass, short.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, UN_IMPL.find(op, opc, ShortVector::unaryOperations)); } @@ -796,7 +800,7 @@ public abstract class ShortVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), null, short.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, null, BIN_IMPL.find(op, opc, ShortVector::binaryOperations)); } @@ -847,7 +851,7 @@ public abstract class ShortVector extends AbstractVector { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), maskClass, short.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, m, BIN_IMPL.find(op, opc, ShortVector::binaryOperations)); } @@ -1034,7 +1038,7 @@ public abstract class ShortVector extends AbstractVector { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), null, short.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, e, null, BIN_INT_IMPL.find(op, opc, ShortVector::broadcastIntOperations)); } @@ -1055,7 +1059,7 @@ public abstract class ShortVector extends AbstractVector { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), maskClass, short.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, e, m, BIN_INT_IMPL.find(op, opc, ShortVector::broadcastIntOperations)); } @@ -1132,7 +1136,7 @@ public abstract class ShortVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), null, short.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, tother, null, TERN_IMPL.find(op, opc, ShortVector::ternaryOperations)); } @@ -1172,7 +1176,7 @@ public abstract class ShortVector extends AbstractVector { } int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), maskClass, short.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, tother, m, TERN_IMPL.find(op, opc, ShortVector::ternaryOperations)); } @@ -2071,7 +2075,7 @@ public abstract class ShortVector extends AbstractVector { that.check(this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, short.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, null, (cond, v0, v1, m1) -> { AbstractMask m @@ -2093,7 +2097,7 @@ public abstract class ShortVector extends AbstractVector { m.check(maskType, this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, short.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, m, (cond, v0, v1, m1) -> { AbstractMask cmpM @@ -2224,7 +2228,7 @@ public abstract class ShortVector extends AbstractVector { blendTemplate(Class maskType, ShortVector v, M m) { v.check(this); return VectorSupport.blend( - getClass(), maskType, short.class, length(), + getClass(), maskType, laneTypeOrdinal(), length(), this, v, m, (v0, v1, m_) -> v0.bOp(v1, m_, (i, a, b) -> b)); } @@ -2241,7 +2245,7 @@ public abstract class ShortVector extends AbstractVector { // make sure VLENGTH*scale doesn't overflow: vsp.checkScale(scale); return VectorSupport.indexVector( - getClass(), short.class, length(), + getClass(), laneTypeOrdinal(), length(), this, scale, vsp, (v, scale_, s) -> { @@ -2433,7 +2437,7 @@ public abstract class ShortVector extends AbstractVector { ShortVector rearrangeTemplate(Class shuffletype, S shuffle) { Objects.requireNonNull(shuffle); return VectorSupport.rearrangeOp( - getClass(), shuffletype, null, short.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2460,7 +2464,7 @@ public abstract class ShortVector extends AbstractVector { Objects.requireNonNull(shuffle); m.check(masktype, this); return VectorSupport.rearrangeOp( - getClass(), shuffletype, masktype, short.class, length(), + getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), this, shuffle, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2486,7 +2490,7 @@ public abstract class ShortVector extends AbstractVector { VectorMask valid = shuffle.laneIsValid(); ShortVector r0 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, short.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v0, s_, m_) -> v0.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); @@ -2494,7 +2498,7 @@ public abstract class ShortVector extends AbstractVector { })); ShortVector r1 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, short.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), v, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2544,7 +2548,7 @@ public abstract class ShortVector extends AbstractVector { ShortVector compressTemplate(Class masktype, M m) { m.check(masktype, this); return (ShortVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_COMPRESS, getClass(), masktype, - short.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> compressHelper(v1, m1)); } @@ -2563,7 +2567,7 @@ public abstract class ShortVector extends AbstractVector { ShortVector expandTemplate(Class masktype, M m) { m.check(masktype, this); return (ShortVector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_EXPAND, getClass(), masktype, - short.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> expandHelper(v1, m1)); } @@ -2578,7 +2582,7 @@ public abstract class ShortVector extends AbstractVector { /*package-private*/ @ForceInline final ShortVector selectFromTemplate(ShortVector v) { - return (ShortVector)VectorSupport.selectFromOp(getClass(), null, short.class, + return (ShortVector)VectorSupport.selectFromOp(getClass(), null, laneTypeOrdinal(), length(), this, v, null, (v1, v2, _m) -> v2.rearrange(v1.toShuffle())); @@ -2598,7 +2602,7 @@ public abstract class ShortVector extends AbstractVector { ShortVector selectFromTemplate(ShortVector v, Class masktype, M m) { m.check(masktype, this); - return (ShortVector)VectorSupport.selectFromOp(getClass(), masktype, short.class, + return (ShortVector)VectorSupport.selectFromOp(getClass(), masktype, laneTypeOrdinal(), length(), this, v, m, (v1, v2, _m) -> v2.rearrange(v1.toShuffle(), _m)); @@ -2616,7 +2620,7 @@ public abstract class ShortVector extends AbstractVector { /*package-private*/ @ForceInline final ShortVector selectFromTemplate(ShortVector v1, ShortVector v2) { - return VectorSupport.selectFromTwoVectorOp(getClass(), short.class, length(), this, v1, v2, + return VectorSupport.selectFromTwoVectorOp(getClass(), laneTypeOrdinal(), length(), this, v1, v2, (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); } @@ -2836,7 +2840,7 @@ public abstract class ShortVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), maskClass, short.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, REDUCE_IMPL.find(op, opc, ShortVector::reductionOperations))); } @@ -2854,7 +2858,7 @@ public abstract class ShortVector extends AbstractVector { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), null, short.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, REDUCE_IMPL.find(op, opc, ShortVector::reductionOperations))); } @@ -3137,7 +3141,7 @@ public abstract class ShortVector extends AbstractVector { } return VectorSupport.loadWithMap( - vectorType, null, short.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), lsp.vectorType(), lsp.length(), a, ARRAY_BASE, vix0, vix1, null, null, null, a, offset, indexMap, mapOffset, vsp, @@ -3470,7 +3474,7 @@ public abstract class ShortVector extends AbstractVector { offset = checkFromIndexSize(offset, length(), a.length); ShortSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, @@ -3619,7 +3623,7 @@ public abstract class ShortVector extends AbstractVector { offset = checkFromIndexSize(offset, length(), a.length); ShortSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, charArrayAddress(a, offset), false, this, a, offset, @@ -3824,7 +3828,7 @@ public abstract class ShortVector extends AbstractVector { ShortVector fromArray0Template(short[] a, int offset) { ShortSpecies vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -3841,7 +3845,7 @@ public abstract class ShortVector extends AbstractVector { m.check(species()); ShortSpecies vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -3888,7 +3892,7 @@ public abstract class ShortVector extends AbstractVector { } return VectorSupport.loadWithMap( - vectorType, maskClass, short.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), lsp.vectorType(), lsp.length(), a, ARRAY_BASE, vix0, vix1, null, null, m, a, offset, indexMap, mapOffset, vsp, @@ -3904,7 +3908,7 @@ public abstract class ShortVector extends AbstractVector { ShortVector fromCharArray0Template(char[] a, int offset) { ShortSpecies vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, charArrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -3921,7 +3925,7 @@ public abstract class ShortVector extends AbstractVector { m.check(species()); ShortSpecies vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, charArrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -3936,7 +3940,7 @@ public abstract class ShortVector extends AbstractVector { ShortVector fromMemorySegment0Template(MemorySegment ms, long offset) { ShortSpecies vsp = vspecies(); return ScopedMemoryAccess.loadFromMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, vsp, (msp, off, s) -> { return s.ldLongOp((MemorySegment) msp, off, ShortVector::memorySegmentGet); @@ -3952,7 +3956,7 @@ public abstract class ShortVector extends AbstractVector { ShortSpecies vsp = vspecies(); m.check(vsp); return ScopedMemoryAccess.loadFromMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, m, vsp, offsetInRange, (msp, off, s, vm) -> { return s.ldLongOp((MemorySegment) msp, off, vm, ShortVector::memorySegmentGet); @@ -3970,7 +3974,7 @@ public abstract class ShortVector extends AbstractVector { void intoArray0Template(short[] a, int offset) { ShortSpecies vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, (arr, off, v) @@ -3987,7 +3991,7 @@ public abstract class ShortVector extends AbstractVector { m.check(species()); ShortSpecies vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -4002,7 +4006,7 @@ public abstract class ShortVector extends AbstractVector { void intoMemorySegment0(MemorySegment ms, long offset) { ShortSpecies vsp = vspecies(); ScopedMemoryAccess.storeIntoMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), this, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v) -> { @@ -4019,7 +4023,7 @@ public abstract class ShortVector extends AbstractVector { ShortSpecies vsp = vspecies(); m.check(vsp); ScopedMemoryAccess.storeIntoMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), this, m, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v, vm) -> { @@ -4037,7 +4041,7 @@ public abstract class ShortVector extends AbstractVector { m.check(species()); ShortSpecies vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, charArrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -4269,7 +4273,7 @@ public abstract class ShortVector extends AbstractVector { final ShortVector broadcastBits(long bits) { return (ShortVector) VectorSupport.fromBitsCoerced( - vectorType, short.class, laneCount, + vectorType, laneTypeOrdinal(), laneCount, bits, MODE_BROADCAST, this, (bits_, s_) -> s_.rvOp(i -> bits_)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java index eb231597e85..13ee9e27e0d 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.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 @@ -207,7 +207,7 @@ public abstract class VectorMask extends jdk.internal.vm.vector.VectorSupport int laneCount = vsp.laneCount(); offset = VectorIntrinsics.checkFromIndexSize(offset, laneCount, bits.length); return VectorSupport.load( - vsp.maskType(), vsp.elementType(), laneCount, + vsp.maskType(), vsp.laneTypeOrdinal(), laneCount, bits, (long) offset + Unsafe.ARRAY_BOOLEAN_BASE_OFFSET, false, bits, offset, vsp, (c, idx, s) @@ -240,7 +240,7 @@ public abstract class VectorMask extends jdk.internal.vm.vector.VectorSupport public static VectorMask fromLong(VectorSpecies species, long bits) { AbstractSpecies vsp = (AbstractSpecies) species; bits = bits & (0xFFFFFFFFFFFFFFFFL >>> (64 - vsp.laneCount())); - return VectorSupport.fromBitsCoerced(vsp.maskType(), vsp.elementType(), vsp.laneCount(), bits, + return VectorSupport.fromBitsCoerced(vsp.maskType(), vsp.laneTypeOrdinal(), vsp.laneCount(), bits, VectorSupport.MODE_BITS_COERCED_LONG_TO_MASK, vsp, (m, s) -> { if (m == (m >> 1)) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java index 34ab05046e0..84009c55ac9 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.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 @@ -817,8 +817,8 @@ public abstract class VectorOperators { private static ConversionImpl convert(String name, char kind, Class dom, Class ran, int opCode, int flags) { - int domran = ((LaneType.of(dom).basicType << VO_DOM_SHIFT) + - (LaneType.of(ran).basicType << VO_RAN_SHIFT)); + int domran = ((LaneType.of(dom).ordinal() << VO_DOM_SHIFT) + + (LaneType.of(ran).ordinal() << VO_RAN_SHIFT)); if (opCode >= 0) { if ((opCode & VO_DOM_RAN_MASK) == 0) { opCode += domran; @@ -945,10 +945,10 @@ public abstract class VectorOperators { @ForceInline /*package-private*/ - boolean compatibleWith(LaneType laneType) { - if (laneType.elementKind == 'F') { + boolean compatibleWith(LaneType type) { + if (type.elementKind == 'F') { return !opKind(VO_NOFP); - } else if (laneType.elementKind == 'I') { + } else if (type.elementKind == 'I') { return !opKind(VO_ONLYFP); } else { throw new AssertionError(); @@ -1077,8 +1077,8 @@ public abstract class VectorOperators { String name; Class domType = dom.elementType; Class ranType = ran.elementType; - int domCode = (dom.basicType << VO_DOM_SHIFT); - int ranCode = (ran.basicType << VO_RAN_SHIFT); + int domCode = (dom.ordinal() << VO_DOM_SHIFT); + int ranCode = (ran.ordinal() << VO_RAN_SHIFT); int opCode = domCode + ranCode; switch (kind) { case 'I': @@ -1156,16 +1156,16 @@ public abstract class VectorOperators { switch (conv.kind) { case 'W': int domCode = (opc >> VO_DOM_SHIFT) & 0xF; - dom = LaneType.ofBasicType(domCode); + dom = LaneType.ofLaneTypeOrdinal(domCode); break; case 'N': int ranCode = (opc >> VO_RAN_SHIFT) & 0xF; - ran = LaneType.ofBasicType(ranCode); + ran = LaneType.ofLaneTypeOrdinal(ranCode); break; } assert((opc & VO_DOM_RAN_MASK) == - ((dom.basicType << VO_DOM_SHIFT) + - (ran.basicType << VO_RAN_SHIFT))); + ((dom.ordinal() << VO_DOM_SHIFT) + + (ran.ordinal() << VO_RAN_SHIFT))); ConversionImpl[] cache = cacheOf(conv.kind, dom); int ranKey = ran.switchKey; if (cache[ranKey] != conv) { @@ -1233,12 +1233,12 @@ public abstract class VectorOperators { break; case 'W': doc = "In-place widen {@code _domVal} inside _ran to {@code (_ran)_domVal}"; - LaneType logdom = LaneType.ofBasicType(domran >> VO_DOM_SHIFT & 0xF); + LaneType logdom = LaneType.ofLaneTypeOrdinal(domran >> VO_DOM_SHIFT & 0xF); doc = doc.replace("_dom", logdom.elementType.getSimpleName()); break; case 'N': doc = "In-place narrow {@code _domVal} to {@code (_ran)_domVal} inside _dom"; - LaneType logran = LaneType.ofBasicType(domran >> VO_RAN_SHIFT & 0xF); + LaneType logran = LaneType.ofLaneTypeOrdinal(domran >> VO_RAN_SHIFT & 0xF); doc = doc.replace("_ran", logran.elementType.getSimpleName()); break; default: diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index ce3b7512c93..03883cf3e8a 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -63,6 +63,10 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { static final ValueLayout.Of$Type$ ELEMENT_LAYOUT = ValueLayout.JAVA_$TYPE$.withByteAlignment(1); + static final int LANE_TYPE_ORDINAL = $laneType$; + + static final int LANEBITS_TYPE_ORDINAL = $lanebitsType$; + @ForceInline static int opCode(Operator op) { return VectorOperators.opCode(op, VO_OPCODE_VALID, FORBID_OPCODE_KIND); @@ -588,11 +592,11 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { public static $abstractvectortype$ zero(VectorSpecies<$Boxtype$> species) { $Type$Species vsp = ($Type$Species) species; #if[FP] - return VectorSupport.fromBitsCoerced(vsp.vectorType(), $type$.class, species.length(), + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), toBits(0.0f), MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); #else[FP] - return VectorSupport.fromBitsCoerced(vsp.vectorType(), $type$.class, species.length(), + return VectorSupport.fromBitsCoerced(vsp.vectorType(), LANE_TYPE_ORDINAL, species.length(), 0, MODE_BROADCAST, vsp, ((bits_, s_) -> s_.rvOp(i -> bits_))); #end[FP] @@ -724,7 +728,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), null, $type$.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, UN_IMPL.find(op, opc, $abstractvectortype$::unaryOperations)); } @@ -759,7 +763,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { } int opc = opCode(op); return VectorSupport.unaryOp( - opc, getClass(), maskClass, $type$.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, UN_IMPL.find(op, opc, $abstractvectortype$::unaryOperations)); } @@ -903,7 +907,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), null, $type$.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, null, BIN_IMPL.find(op, opc, $abstractvectortype$::binaryOperations)); } @@ -970,7 +974,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { int opc = opCode(op); return VectorSupport.binaryOp( - opc, getClass(), maskClass, $type$.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, m, BIN_IMPL.find(op, opc, $abstractvectortype$::binaryOperations)); } @@ -1198,7 +1202,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), null, $type$.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, e, null, BIN_INT_IMPL.find(op, opc, $abstractvectortype$::broadcastIntOperations)); } @@ -1219,7 +1223,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { e &= SHIFT_MASK; int opc = opCode(op); return VectorSupport.broadcastInt( - opc, getClass(), maskClass, $type$.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, e, m, BIN_INT_IMPL.find(op, opc, $abstractvectortype$::broadcastIntOperations)); } @@ -1303,7 +1307,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { #end[BITWISE] int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), null, $type$.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, that, tother, null, TERN_IMPL.find(op, opc, $abstractvectortype$::ternaryOperations)); } @@ -1345,7 +1349,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { #end[BITWISE] int opc = opCode(op); return VectorSupport.ternaryOp( - opc, getClass(), maskClass, $type$.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, that, tother, m, TERN_IMPL.find(op, opc, $abstractvectortype$::ternaryOperations)); } @@ -2477,7 +2481,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { that.check(this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, $type$.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, null, (cond, v0, v1, m1) -> { AbstractMask<$Boxtype$> m @@ -2499,7 +2503,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { m.check(maskType, this); int opc = opCode(op); return VectorSupport.compare( - opc, getClass(), maskType, $type$.class, length(), + opc, getClass(), maskType, laneTypeOrdinal(), length(), this, that, m, (cond, v0, v1, m1) -> { AbstractMask<$Boxtype$> cmpM @@ -2634,7 +2638,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { blendTemplate(Class maskType, $abstractvectortype$ v, M m) { v.check(this); return VectorSupport.blend( - getClass(), maskType, $type$.class, length(), + getClass(), maskType, laneTypeOrdinal(), length(), this, v, m, (v0, v1, m_) -> v0.bOp(v1, m_, (i, a, b) -> b)); } @@ -2651,7 +2655,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { // make sure VLENGTH*scale doesn't overflow: vsp.checkScale(scale); return VectorSupport.indexVector( - getClass(), $type$.class, length(), + getClass(), laneTypeOrdinal(), length(), this, scale, vsp, (v, scale_, s) -> { @@ -2865,7 +2869,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $abstractvectortype$ rearrangeTemplate(Class shuffletype, S shuffle) { Objects.requireNonNull(shuffle); return VectorSupport.rearrangeOp( - getClass(), shuffletype, null, $type$.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2892,7 +2896,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { Objects.requireNonNull(shuffle); m.check(masktype, this); return VectorSupport.rearrangeOp( - getClass(), shuffletype, masktype, $type$.class, length(), + getClass(), shuffletype, masktype, laneTypeOrdinal(), length(), this, shuffle, m, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2918,7 +2922,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { VectorMask<$Boxtype$> valid = shuffle.laneIsValid(); $abstractvectortype$ r0 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, $type$.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), this, shuffle, null, (v0, s_, m_) -> v0.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v0.length()); @@ -2926,7 +2930,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { })); $abstractvectortype$ r1 = VectorSupport.rearrangeOp( - getClass(), shuffletype, null, $type$.class, length(), + getClass(), shuffletype, null, laneTypeOrdinal(), length(), v, shuffle, null, (v1, s_, m_) -> v1.uOp((i, a) -> { int ei = Integer.remainderUnsigned(s_.laneSource(i), v1.length()); @@ -2988,7 +2992,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $Type$Vector compressTemplate(Class masktype, M m) { m.check(masktype, this); return ($Type$Vector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_COMPRESS, getClass(), masktype, - $type$.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> compressHelper(v1, m1)); } @@ -3007,7 +3011,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $Type$Vector expandTemplate(Class masktype, M m) { m.check(masktype, this); return ($Type$Vector) VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_EXPAND, getClass(), masktype, - $type$.class, length(), this, m, + laneTypeOrdinal(), length(), this, m, (v1, m1) -> expandHelper(v1, m1)); } @@ -3022,7 +3026,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { /*package-private*/ @ForceInline final $abstractvectortype$ selectFromTemplate($abstractvectortype$ v) { - return ($Type$Vector)VectorSupport.selectFromOp(getClass(), null, $type$.class, + return ($Type$Vector)VectorSupport.selectFromOp(getClass(), null, laneTypeOrdinal(), length(), this, v, null, (v1, v2, _m) -> v2.rearrange(v1.toShuffle())); @@ -3042,7 +3046,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $abstractvectortype$ selectFromTemplate($abstractvectortype$ v, Class masktype, M m) { m.check(masktype, this); - return ($Type$Vector)VectorSupport.selectFromOp(getClass(), masktype, $type$.class, + return ($Type$Vector)VectorSupport.selectFromOp(getClass(), masktype, laneTypeOrdinal(), length(), this, v, m, (v1, v2, _m) -> v2.rearrange(v1.toShuffle(), _m)); @@ -3060,7 +3064,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { /*package-private*/ @ForceInline final $abstractvectortype$ selectFromTemplate($abstractvectortype$ v1, $abstractvectortype$ v2) { - return VectorSupport.selectFromTwoVectorOp(getClass(), $type$.class, length(), this, v1, v2, + return VectorSupport.selectFromTwoVectorOp(getClass(), laneTypeOrdinal(), length(), this, v1, v2, (vec1, vec2, vec3) -> selectFromTwoVectorHelper(vec1, vec2, vec3)); } @@ -3409,7 +3413,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), maskClass, $type$.class, length(), + opc, getClass(), maskClass, laneTypeOrdinal(), length(), this, m, REDUCE_IMPL.find(op, opc, $abstractvectortype$::reductionOperations))); } @@ -3427,7 +3431,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { } int opc = opCode(op); return fromBits(VectorSupport.reductionCoerced( - opc, getClass(), null, $type$.class, length(), + opc, getClass(), null, laneTypeOrdinal(), length(), this, null, REDUCE_IMPL.find(op, opc, $abstractvectortype$::reductionOperations))); } @@ -3789,7 +3793,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { } return VectorSupport.loadWithMap( - vectorType, null, $type$.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), lsp.vectorType(), lsp.length(), a, ARRAY_BASE, vix0, vix1, vix2, vix3, null, a, offset, indexMap, mapOffset, vsp, @@ -3797,7 +3801,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { s.vOp(n -> c[idx + iMap[idy+n]])); #else[byte] return VectorSupport.loadWithMap( - vectorType, null, $type$.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), lsp.vectorType(), lsp.length(), a, ARRAY_BASE, vix0, vix1, null, null, null, a, offset, indexMap, mapOffset, vsp, @@ -3850,7 +3854,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, null, $type$.class, vsp.laneCount(), + vectorType, null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, null, a, offset, indexMap, mapOffset, vsp, @@ -4349,7 +4353,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { offset = checkFromIndexSize(offset, length(), a.length); $Type$Species vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, @@ -4476,7 +4480,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), null, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), null, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, null, @@ -4569,7 +4573,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { offset = checkFromIndexSize(offset, length(), a.length); $Type$Species vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, charArrayAddress(a, offset), false, this, a, offset, @@ -4731,7 +4735,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $Type$Species vsp = vspecies(); ByteVector normalized = this.and((byte) 1); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, booleanArrayAddress(a, offset), false, normalized, a, offset, @@ -4945,7 +4949,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $abstractvectortype$ fromArray0Template($type$[] a, int offset) { $Type$Species vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -4962,7 +4966,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { m.check(species()); $Type$Species vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -5020,7 +5024,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { } return VectorSupport.loadWithMap( - vectorType, maskClass, $type$.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), lsp.vectorType(), lsp.length(), a, ARRAY_BASE, vix0, vix1, vix2, vix3, m, a, offset, indexMap, mapOffset, vsp, @@ -5028,7 +5032,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { s.vOp(vm, n -> c[idx + iMap[idy+n]])); #else[byte] return VectorSupport.loadWithMap( - vectorType, maskClass, $type$.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), lsp.vectorType(), lsp.length(), a, ARRAY_BASE, vix0, vix1, null, null, m, a, offset, indexMap, mapOffset, vsp, @@ -5083,7 +5087,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { vix = VectorIntrinsics.checkIndex(vix, a.length); return VectorSupport.loadWithMap( - vectorType, maskClass, $type$.class, vsp.laneCount(), + vectorType, maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, ARRAY_BASE, vix, null, null, null, m, a, offset, indexMap, mapOffset, vsp, @@ -5101,7 +5105,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $abstractvectortype$ fromCharArray0Template(char[] a, int offset) { $Type$Species vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, charArrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -5118,7 +5122,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { m.check(species()); $Type$Species vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, charArrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -5135,7 +5139,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $abstractvectortype$ fromBooleanArray0Template(boolean[] a, int offset) { $Type$Species vsp = vspecies(); return VectorSupport.load( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, booleanArrayAddress(a, offset), false, a, offset, vsp, (arr, off, s) -> s.ldOp(arr, (int) off, @@ -5152,7 +5156,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { m.check(species()); $Type$Species vsp = vspecies(); return VectorSupport.loadMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, booleanArrayAddress(a, offset), false, m, offsetInRange, a, offset, vsp, (arr, off, s, vm) -> s.ldOp(arr, (int) off, vm, @@ -5167,7 +5171,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $abstractvectortype$ fromMemorySegment0Template(MemorySegment ms, long offset) { $Type$Species vsp = vspecies(); return ScopedMemoryAccess.loadFromMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, vsp, (msp, off, s) -> { return s.ldLongOp((MemorySegment) msp, off, $abstractvectortype$::memorySegmentGet); @@ -5183,7 +5187,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $Type$Species vsp = vspecies(); m.check(vsp); return ScopedMemoryAccess.loadFromMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), (AbstractMemorySegmentImpl) ms, offset, m, vsp, offsetInRange, (msp, off, s, vm) -> { return s.ldLongOp((MemorySegment) msp, off, vm, $abstractvectortype$::memorySegmentGet); @@ -5201,7 +5205,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { void intoArray0Template($type$[] a, int offset) { $Type$Species vsp = vspecies(); VectorSupport.store( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, a, offset, (arr, off, v) @@ -5218,7 +5222,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { m.check(species()); $Type$Species vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, arrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -5274,7 +5278,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { vix = VectorIntrinsics.checkIndex(vix, a.length); VectorSupport.storeWithMap( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), isp.vectorType(), isp.length(), a, arrayAddress(a, 0), vix, this, m, @@ -5299,7 +5303,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $Type$Species vsp = vspecies(); ByteVector normalized = this.and((byte) 1); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, booleanArrayAddress(a, offset), false, normalized, m, a, offset, (arr, off, v, vm) @@ -5313,7 +5317,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { void intoMemorySegment0(MemorySegment ms, long offset) { $Type$Species vsp = vspecies(); ScopedMemoryAccess.storeIntoMemorySegment( - vsp.vectorType(), vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), LANE_TYPE_ORDINAL, vsp.laneCount(), this, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v) -> { @@ -5330,7 +5334,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { $Type$Species vsp = vspecies(); m.check(vsp); ScopedMemoryAccess.storeIntoMemorySegmentMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), this, m, (AbstractMemorySegmentImpl) ms, offset, (msp, off, v, vm) -> { @@ -5349,7 +5353,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { m.check(species()); $Type$Species vsp = vspecies(); VectorSupport.storeMasked( - vsp.vectorType(), maskClass, vsp.elementType(), vsp.laneCount(), + vsp.vectorType(), maskClass, LANE_TYPE_ORDINAL, vsp.laneCount(), a, charArrayAddress(a, offset), false, this, m, a, offset, (arr, off, v, vm) @@ -5604,7 +5608,7 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> { final $abstractvectortype$ broadcastBits(long bits) { return ($abstractvectortype$) VectorSupport.fromBitsCoerced( - vectorType, $type$.class, laneCount, + vectorType, laneTypeOrdinal(), laneCount, bits, MODE_BROADCAST, this, (bits_, s_) -> s_.rvOp(i -> bits_)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template index fa502e3f29a..61ac02d84f6 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-VectorBits.java.template @@ -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 @@ -119,6 +119,13 @@ final class $vectortype$ extends $abstractvectortype$ { return ($type$[])getPayload(); } + /*package-private*/ + @ForceInline + final @Override + int laneTypeOrdinal() { + return LANE_TYPE_ORDINAL; + } + // Virtualized constructors @Override @@ -577,7 +584,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public $bitstype$ laneHelper(int i) { return ($bitstype$) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { $type$[] vecarr = vec.vec(); @@ -627,7 +634,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public $vectortype$ withLaneHelper(int i, $type$ e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)$Type$.$type$ToRaw$Bitstype$Bits(e), (v, ix, bits) -> { $type$[] res = v.vec().clone(); @@ -730,7 +737,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public $type$ laneHelper(int i) { return ($type$) VectorSupport.extract( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (vec, ix) -> { $type$[] vecarr = vec.vec(); @@ -832,7 +839,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public $vectortype$ withLaneHelper(int i, $type$ e) { return VectorSupport.insert( - VCLASS, ETYPE, VLENGTH, + VCLASS, LANE_TYPE_ORDINAL, VLENGTH, this, i, (long)e, (v, ix, bits) -> { $type$[] res = v.vec().clone(); @@ -937,8 +944,8 @@ final class $vectortype$ extends $abstractvectortype$ { throw new IllegalArgumentException("VectorMask length and species length differ"); return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST, - this.getClass(), ETYPE, VLENGTH, - species.maskType(), species.elementType(), VLENGTH, + this.getClass(), LANE_TYPE_ORDINAL, VLENGTH, + species.maskType(), species.laneTypeOrdinal(), VLENGTH, this, species, (m, s) -> s.maskFactory(m.toArray()).check(s)); } @@ -948,7 +955,7 @@ final class $vectortype$ extends $abstractvectortype$ { /*package-private*/ $masktype$ indexPartiallyInUpperRange(long offset, long limit) { return ($masktype$) VectorSupport.indexPartiallyInUpperRange( - $masktype$.class, $type$.class, VLENGTH, offset, limit, + $masktype$.class, LANE_TYPE_ORDINAL, VLENGTH, offset, limit, (o, l) -> ($masktype$) TRUE_MASK.indexPartiallyInRange(o, l)); } @@ -964,7 +971,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public $masktype$ compress() { return ($masktype$)VectorSupport.compressExpandOp(VectorSupport.VECTOR_OP_MASK_COMPRESS, - $vectortype$.class, $masktype$.class, ETYPE, VLENGTH, null, this, + $vectortype$.class, $masktype$.class, LANE_TYPE_ORDINAL, VLENGTH, null, this, (v1, m1) -> VSPECIES.iota().compare(VectorOperators.LT, m1.trueCount())); } @@ -976,7 +983,7 @@ final class $vectortype$ extends $abstractvectortype$ { public $masktype$ and(VectorMask<$Boxtype$> mask) { Objects.requireNonNull(mask); $masktype$ m = ($masktype$)mask; - return VectorSupport.binaryOp(VECTOR_OP_AND, $masktype$.class, null, $bitstype$.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_AND, $masktype$.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a & b)); } @@ -986,7 +993,7 @@ final class $vectortype$ extends $abstractvectortype$ { public $masktype$ or(VectorMask<$Boxtype$> mask) { Objects.requireNonNull(mask); $masktype$ m = ($masktype$)mask; - return VectorSupport.binaryOp(VECTOR_OP_OR, $masktype$.class, null, $bitstype$.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_OR, $masktype$.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a | b)); } @@ -996,7 +1003,7 @@ final class $vectortype$ extends $abstractvectortype$ { public $masktype$ xor(VectorMask<$Boxtype$> mask) { Objects.requireNonNull(mask); $masktype$ m = ($masktype$)mask; - return VectorSupport.binaryOp(VECTOR_OP_XOR, $masktype$.class, null, $bitstype$.class, VLENGTH, + return VectorSupport.binaryOp(VECTOR_OP_XOR, $masktype$.class, null, LANEBITS_TYPE_ORDINAL, VLENGTH, this, m, null, (m1, m2, vm) -> m1.bOp(m2, (i, a, b) -> a ^ b)); } @@ -1006,21 +1013,21 @@ final class $vectortype$ extends $abstractvectortype$ { @Override @ForceInline public int trueCount() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, $masktype$.class, $bitstype$.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TRUECOUNT, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> trueCountHelper(m.getBits())); } @Override @ForceInline public int firstTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, $masktype$.class, $bitstype$.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_FIRSTTRUE, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> firstTrueHelper(m.getBits())); } @Override @ForceInline public int lastTrue() { - return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, $masktype$.class, $bitstype$.class, VLENGTH, this, + return (int) VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_LASTTRUE, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> lastTrueHelper(m.getBits())); } @@ -1030,7 +1037,7 @@ final class $vectortype$ extends $abstractvectortype$ { if (length() > Long.SIZE) { throw new UnsupportedOperationException("too many lanes for one long"); } - return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, $masktype$.class, $bitstype$.class, VLENGTH, this, + return VectorSupport.maskReductionCoerced(VECTOR_OP_MASK_TOLONG, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, (m) -> toLongHelper(m.getBits())); } @@ -1040,7 +1047,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline public boolean laneIsSet(int i) { Objects.checkIndex(i, length()); - return VectorSupport.extract($masktype$.class, $type$.class, VLENGTH, + return VectorSupport.extract($masktype$.class, LANE_TYPE_ORDINAL, VLENGTH, this, i, (m, idx) -> (m.getBits()[idx] ? 1L : 0L)) == 1L; } @@ -1049,7 +1056,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override @ForceInline public boolean anyTrue() { - return VectorSupport.test(BT_ne, $masktype$.class, $bitstype$.class, VLENGTH, + return VectorSupport.test(BT_ne, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> anyTrueHelper((($masktype$)m).getBits())); } @@ -1057,7 +1064,7 @@ final class $vectortype$ extends $abstractvectortype$ { @Override @ForceInline public boolean allTrue() { - return VectorSupport.test(BT_overflow, $masktype$.class, $bitstype$.class, VLENGTH, + return VectorSupport.test(BT_overflow, $masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, this, vspecies().maskAll(true), (m, __) -> allTrueHelper((($masktype$)m).getBits())); } @@ -1065,7 +1072,7 @@ final class $vectortype$ extends $abstractvectortype$ { @ForceInline /*package-private*/ static $masktype$ maskAll(boolean bit) { - return VectorSupport.fromBitsCoerced($masktype$.class, $bitstype$.class, VLENGTH, + return VectorSupport.fromBitsCoerced($masktype$.class, LANEBITS_TYPE_ORDINAL, VLENGTH, (bit ? -1 : 0), MODE_BROADCAST, null, (v, __) -> (v != 0 ? TRUE_MASK : FALSE_MASK)); } diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh index 6841a47c757..a9f1648eaa7 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/gen-src.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# 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 @@ -77,11 +77,15 @@ do byte) Wideboxtype=Integer sizeInBytes=1 + laneType=LT_BYTE + lanebitsType=LT_BYTE args="$args -KbyteOrShort" ;; short) Wideboxtype=Integer sizeInBytes=2 + laneType=LT_SHORT + lanebitsType=LT_SHORT args="$args -KbyteOrShort" ;; int) @@ -92,6 +96,8 @@ do Fptype=Float Boxfptype=Float sizeInBytes=4 + laneType=LT_INT + lanebitsType=LT_INT args="$args -KintOrLong -KintOrFP -KintOrFloat" ;; long) @@ -99,6 +105,8 @@ do Fptype=Double Boxfptype=Double sizeInBytes=8 + laneType=LT_LONG + lanebitsType=LT_LONG args="$args -KintOrLong -KlongOrDouble" ;; float) @@ -107,6 +115,8 @@ do Bitstype=Int Boxbitstype=Integer sizeInBytes=4 + laneType=LT_FLOAT + lanebitsType=LT_INT args="$args -KintOrFP -KintOrFloat" ;; double) @@ -115,11 +125,13 @@ do Bitstype=Long Boxbitstype=Long sizeInBytes=8 + laneType=LT_DOUBLE + lanebitsType=LT_LONG args="$args -KintOrFP -KlongOrDouble" ;; esac - args="$args -K$kind -DBoxtype=$Boxtype -DWideboxtype=$Wideboxtype" + args="$args -K$kind -DlaneType=$laneType -DlanebitsType=$lanebitsType -DBoxtype=$Boxtype -DWideboxtype=$Wideboxtype" args="$args -Dbitstype=$bitstype -DBitstype=$Bitstype -DBoxbitstype=$Boxbitstype" args="$args -Dfptype=$fptype -DFptype=$Fptype -DBoxfptype=$Boxfptype" args="$args -DsizeInBytes=$sizeInBytes" From a69409b0b7bcb4eb9a66327e1c6c53b3361ea1e9 Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Fri, 6 Feb 2026 03:26:16 +0000 Subject: [PATCH 145/215] 8374516: -version asserts with "-XX:+UseAESCTRIntrinsics -XX:-UseAES": "need AES instructions and misaligned SSE support" in generate_counterMode_AESCrypt_Parallel() Reviewed-by: kvn --- src/hotspot/cpu/x86/vm_version_x86.cpp | 12 ++++-- ...UseAESCTRIntrinsicsWithUseAESDisabled.java | 39 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/cpuflags/TestUseAESCTRIntrinsicsWithUseAESDisabled.java diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 74df41f8682..eb401e4f877 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1152,6 +1152,10 @@ void VM_Version::get_processor_features() { warning("AES intrinsics require UseAES flag to be enabled. Intrinsics will be disabled."); } FLAG_SET_DEFAULT(UseAESIntrinsics, false); + if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { + warning("AES_CTR intrinsics require UseAES flag to be enabled. AES_CTR intrinsics will be disabled."); + } + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); } else { if (UseSSE > 2) { if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { @@ -1170,8 +1174,8 @@ void VM_Version::get_processor_features() { if (!UseAESIntrinsics) { if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { warning("AES-CTR intrinsics require UseAESIntrinsics flag to be enabled. Intrinsics will be disabled."); - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); } + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); } else { if (supports_sse4_1()) { if (FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { @@ -1191,16 +1195,16 @@ void VM_Version::get_processor_features() { } else if (UseAES || UseAESIntrinsics || UseAESCTRIntrinsics) { if (UseAES && !FLAG_IS_DEFAULT(UseAES)) { warning("AES instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseAES, false); } + FLAG_SET_DEFAULT(UseAES, false); if (UseAESIntrinsics && !FLAG_IS_DEFAULT(UseAESIntrinsics)) { warning("AES intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseAESIntrinsics, false); } + FLAG_SET_DEFAULT(UseAESIntrinsics, false); if (UseAESCTRIntrinsics && !FLAG_IS_DEFAULT(UseAESCTRIntrinsics)) { warning("AES-CTR intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); } + FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false); } // Use CLMUL instructions if available. diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestUseAESCTRIntrinsicsWithUseAESDisabled.java b/test/hotspot/jtreg/compiler/cpuflags/TestUseAESCTRIntrinsicsWithUseAESDisabled.java new file mode 100644 index 00000000000..b6c28bb6a3b --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/TestUseAESCTRIntrinsicsWithUseAESDisabled.java @@ -0,0 +1,39 @@ +/* + * 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 8374516 + * @summary Regression test for -XX:+UseAESCTRIntrinsics -XX:-UseAES crash + * @requires os.arch=="amd64" | os.arch=="x86_64" + * @requires vm.debug + * @run main/othervm -XX:+UseAESCTRIntrinsics -XX:-UseAES compiler.cpuflags.TestUseAESCTRIntrinsicsWithUseAESDisabled + */ +package compiler.cpuflags; + +public class TestUseAESCTRIntrinsicsWithUseAESDisabled { + + public static void main(String[] args) { + System.out.println("passed"); + } +} \ No newline at end of file From 57241545c63469a9301bd191c49336582a29e306 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Fri, 6 Feb 2026 06:43:02 +0000 Subject: [PATCH 146/215] 8376052: Use AttachOperationFailedException rather than AttachNotSupportedException in findTargetProcessTmpDirectory() Reviewed-by: alanb, amenkov --- .../sun/tools/attach/VirtualMachineImpl.java | 55 +++++++++++-------- .../attach/AttachNotSupportedException.java | 13 ----- .../AttachOperationFailedException.java | 16 +++++- 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java index 998e8d037b4..4a1cd3aba78 100644 --- a/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java +++ b/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java @@ -26,11 +26,13 @@ package sun.tools.attach; import com.sun.tools.attach.AgentLoadException; import com.sun.tools.attach.AttachNotSupportedException; +import com.sun.tools.attach.AttachOperationFailedException; import com.sun.tools.attach.spi.AttachProvider; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -42,6 +44,7 @@ import java.util.regex.Pattern; import static java.nio.charset.StandardCharsets.UTF_8; import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.MonitorException; /* * Linux implementation of HotSpotVirtualMachine @@ -251,24 +254,26 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { return f; } - private String findTargetProcessTmpDirectory(long pid) throws AttachNotSupportedException { + private String findTargetProcessTmpDirectory(long pid) throws IOException { final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP); /* We need to handle at least 4 different cases: - * 1. Caller and target processes share PID namespace and root filesystem (host to host or container to - * container with both /tmp mounted between containers). - * 2. Caller and target processes share PID namespace and root filesystem but the target process has elevated - * privileges (host to host). - * 3. Caller and target processes share PID namespace but NOT root filesystem (container to container). - * 4. Caller and target processes share neither PID namespace nor root filesystem (host to container) + * 1. Caller and target processes share PID namespace and root + * filesystem (host to host or container to container with both /tmp + * mounted between containers). + * 2. Caller and target processes share PID namespace and root + * filesystem but the target process has elevated privileges + * (host to host). + * 3. Caller and target processes share PID namespace but NOT root + * filesystem (container to container). + * 4. Caller and target processes share neither PID namespace nor root + * filesystem (host to container) * - * if target is elevated, we cant use /proc//... so we have to fallback to /tmp, but that may not be shared - * with the target/attachee process, so we should check whether /tmp on both is same. This method would throw - * AttachNotSupportedException if they are different because we cannot make a connection with target VM. - * - * In addition, we can also check the target pid's signal masks to see if it catches SIGQUIT and only do so if in - * fact it does ... this reduces the risk of killing an innocent process in the current ns as opposed to - * attaching to the actual target JVM ... c.f: checkCatchesAndSendQuitTo() below. + * if target is elevated, we cant use /proc//... so we have to + * fallback to /tmp, but that may not be shared with the target/attachee + * process, so we should check whether /tmp on both is same. This method + * would throw AttachOperationFailedException if they are different + * because we cannot make a connection with target VM. */ try { @@ -277,7 +282,7 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { } else if (Files.isSameFile(tmpOnProcPidRoot, TMPDIR)) { return TMPDIR.toString(); } else { - throw new AttachNotSupportedException("Unable to access the filesystem of the target process"); + throw new AttachOperationFailedException("Unable to access the filesystem of the target process"); } } catch (IOException ioe) { try { @@ -290,15 +295,19 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { // even if we cannot access /proc//root. // The process with capsh/setcap would fall this pattern. return TMPDIR.toString(); - } else { - throw new AttachNotSupportedException("Unable to access the filesystem of the target process", ioe); } - } catch (AttachNotSupportedException e) { - // AttachNotSupportedException happened in above should go through - throw e; - } catch (Exception e) { - // Other exceptions would be wrapped with AttachNotSupportedException - throw new AttachNotSupportedException("Unable to access the filesystem of the target process", e); + // Throw original IOE if target process not found on localhost. + throw ioe; + } catch (URISyntaxException e) { + // URISyntaxException is defined as a checked exception at + // MonitoredHost.getMonitoredHost() if the URI string poorly + // formed. However "//localhost" is hard-coded at here, so the + // exception should not happen. + throw new AssertionError("Unexpected exception", e); + } catch (MonitorException e) { + // Other exceptions (happened at MonitoredHost) would be wrapped + // with AttachOperationFailedException. + throw new AttachOperationFailedException("Unable to find target proces", e); } } } diff --git a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java index 725db3e7732..050c1030bf6 100644 --- a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java +++ b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachNotSupportedException.java @@ -62,17 +62,4 @@ public class AttachNotSupportedException extends Exception { super(s); } - /** - * Constructs an AttachNotSupportedException with - * the specified cause. - * - * @param message the detail message. - * @param cause the cause of this exception. - * - * @since 27 - */ - public AttachNotSupportedException(String message, Throwable cause) { - super(message, cause); - } - } diff --git a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachOperationFailedException.java b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachOperationFailedException.java index cbc4410907c..3f26c02c304 100644 --- a/src/jdk.attach/share/classes/com/sun/tools/attach/AttachOperationFailedException.java +++ b/src/jdk.attach/share/classes/com/sun/tools/attach/AttachOperationFailedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 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 @@ -50,4 +50,18 @@ public class AttachOperationFailedException extends IOException { public AttachOperationFailedException(String message) { super(message); } + + /** + * Constructs an AttachOperationFailedException with + * the specified cause. + * + * @param message the detail message. + * @param cause the cause of this exception. + * + * @since 27 + */ + public AttachOperationFailedException(String message, Throwable cause) { + super(message, cause); + } + } From 90f370235c0f774450f4548886f95606b0035a25 Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Fri, 6 Feb 2026 07:13:58 +0000 Subject: [PATCH 147/215] 8377226: Inline Thread::set_allocated_bytes Reviewed-by: jsjolen, phubner --- src/hotspot/share/runtime/thread.cpp | 2 +- src/hotspot/share/runtime/thread.hpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index bfbd4727e9a..355dc70ae39 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -92,7 +92,7 @@ Thread::Thread(MemTag mem_tag) { DEBUG_ONLY(_owned_locks = nullptr;) NOT_PRODUCT(_skip_gcalot = false;) _jvmti_env_iteration_count = 0; - set_allocated_bytes(0); + _allocated_bytes = 0; _current_pending_raw_monitor = nullptr; _vm_error_callbacks = nullptr; diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index dcd6fb2d3fd..29e3c07ba20 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -410,7 +410,6 @@ class Thread: public ThreadShadow { void fill_tlab(HeapWord* start, size_t pre_reserved, size_t new_size); jlong allocated_bytes() { return _allocated_bytes; } - void set_allocated_bytes(jlong value) { _allocated_bytes = value; } void incr_allocated_bytes(jlong size) { _allocated_bytes += size; } inline jlong cooked_allocated_bytes(); From bfd5bdaf7ffd96cf887fbbfe072414be38ee1b84 Mon Sep 17 00:00:00 2001 From: Damon Fenacci Date: Fri, 6 Feb 2026 07:44:25 +0000 Subject: [PATCH 148/215] 8374582: [REDO] Move input validation checks to Java for java.lang.StringCoding intrinsics Co-authored-by: Volkan Yazici Reviewed-by: chagedorn, thartmann, vyazici --- .../cpu/aarch64/macroAssembler_aarch64.cpp | 14 ++- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 14 ++- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 66 +++++++------ src/hotspot/share/classfile/vmIntrinsics.hpp | 6 +- src/hotspot/share/opto/classes.hpp | 2 +- src/hotspot/share/opto/escape.cpp | 2 +- src/hotspot/share/opto/graphKit.cpp | 4 +- src/hotspot/share/opto/library_call.cpp | 60 +++++++++--- src/hotspot/share/opto/library_call.hpp | 11 ++- src/hotspot/share/opto/loopTransform.cpp | 4 +- src/hotspot/share/opto/loopopts.cpp | 12 +-- src/hotspot/share/opto/macro.cpp | 10 +- src/hotspot/share/opto/node.hpp | 6 +- src/hotspot/share/opto/opaquenode.cpp | 10 +- src/hotspot/share/opto/opaquenode.hpp | 32 +++++-- src/hotspot/share/opto/split_if.cpp | 8 +- .../share/classes/java/lang/String.java | 4 +- .../share/classes/java/lang/StringCoding.java | 93 ++++++++++++++++--- .../share/classes/java/lang/System.java | 4 +- .../jdk/internal/access/JavaLangAccess.java | 18 ++-- .../share/classes/sun/nio/cs/CESU_8.java | 4 +- .../share/classes/sun/nio/cs/DoubleByte.java | 2 +- .../share/classes/sun/nio/cs/ISO_8859_1.java | 40 ++++---- .../share/classes/sun/nio/cs/SingleByte.java | 2 +- .../share/classes/sun/nio/cs/US_ASCII.java | 2 +- .../share/classes/sun/nio/cs/UTF_8.java | 4 +- .../sun/nio/cs/ext/EUC_JP.java.template | 4 +- .../TestCanReduceCheckUsersDifferentIfs.java | 6 +- .../intrinsics/string/TestCountPositives.java | 2 +- .../string/TestEncodeIntrinsics.java | 2 +- .../intrinsics/string/TestHasNegatives.java | 2 +- .../string/TestOpaqueConstantBoolNodes.java | 60 ++++++++++++ .../intrinsics/string/TestRangeCheck.java | 68 ++++++++++++++ .../compiler/lib/ir_framework/IRNode.java | 5 + .../patches/java.base/java/lang/Helper.java | 2 +- .../jtreg/compiler/unsafe/OpaqueAccesses.java | 12 +-- 36 files changed, 442 insertions(+), 155 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java create mode 100644 test/hotspot/jtreg/compiler/intrinsics/string/TestRangeCheck.java diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index f8b5a6f825c..ba42602ddc1 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -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, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -6419,10 +6419,14 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value) // Intrinsic for // -// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray -// return the number of characters copied. -// - java/lang/StringUTF16.compress -// return index of non-latin1 character if copy fails, otherwise 'len'. +// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ASCII // // This version always returns the number of characters copied, and does not // clobber the 'len' register. A successful copy will complete with the post- diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index 824ea872935..b4e0ba69042 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.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. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2813,10 +2813,14 @@ void C2_MacroAssembler::char_array_compress_v(Register src, Register dst, Regist // Intrinsic for // -// - sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray -// return the number of characters copied. -// - java/lang/StringUTF16.compress -// return index of non-latin1 character if copy fails, otherwise 'len'. +// - sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// Encodes byte[] (containing UTF-16) to byte[] in ISO-8859-1 +// +// - java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) +// Encodes char[] to byte[] in ASCII // // This version always returns the number of characters copied. A successful // copy will complete with the post-condition: 'res' == 'len', while an diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 7f7bb2c4c7f..b88f510401a 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -6251,32 +6251,46 @@ void MacroAssembler::evpbroadcast(BasicType type, XMMRegister dst, Register src, } } -// encode char[] to byte[] in ISO_8859_1 or ASCII - //@IntrinsicCandidate - //private static int implEncodeISOArray(byte[] sa, int sp, - //byte[] da, int dp, int len) { - // int i = 0; - // for (; i < len; i++) { - // char c = StringUTF16.getChar(sa, sp++); - // if (c > '\u00FF') - // break; - // da[dp++] = (byte)c; - // } - // return i; - //} - // - //@IntrinsicCandidate - //private static int implEncodeAsciiArray(char[] sa, int sp, - // byte[] da, int dp, int len) { - // int i = 0; - // for (; i < len; i++) { - // char c = sa[sp++]; - // if (c >= '\u0080') - // break; - // da[dp++] = (byte)c; - // } - // return i; - //} +// Encode given char[]/byte[] to byte[] in ISO_8859_1 or ASCII +// +// @IntrinsicCandidate +// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0( +// char[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = sa[sp++]; +// if (c > '\u00FF') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } +// +// @IntrinsicCandidate +// int java.lang.StringCoding.encodeISOArray0( +// byte[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = StringUTF16.getChar(sa, sp++); +// if (c > '\u00FF') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } +// +// @IntrinsicCandidate +// int java.lang.StringCoding.encodeAsciiArray0( +// char[] sa, int sp, byte[] da, int dp, int len) { +// int i = 0; +// for (; i < len; i++) { +// char c = sa[sp++]; +// if (c >= '\u0080') +// break; +// da[dp++] = (byte) c; +// } +// return i; +// } void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, XMMRegister tmp1Reg, XMMRegister tmp2Reg, XMMRegister tmp3Reg, XMMRegister tmp4Reg, diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 6e59e149482..75592fd61c8 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -415,18 +415,18 @@ class methodHandle; \ do_class(java_lang_StringCoding, "java/lang/StringCoding") \ do_intrinsic(_countPositives, java_lang_StringCoding, countPositives_name, countPositives_signature, F_S) \ - do_name( countPositives_name, "countPositives") \ + do_name( countPositives_name, "countPositives0") \ do_signature(countPositives_signature, "([BII)I") \ \ do_class(sun_nio_cs_iso8859_1_Encoder, "sun/nio/cs/ISO_8859_1$Encoder") \ do_intrinsic(_encodeISOArray, sun_nio_cs_iso8859_1_Encoder, encodeISOArray_name, encodeISOArray_signature, F_S) \ - do_name( encodeISOArray_name, "implEncodeISOArray") \ + do_name( encodeISOArray_name, "encodeISOArray0") \ do_signature(encodeISOArray_signature, "([CI[BII)I") \ \ do_intrinsic(_encodeByteISOArray, java_lang_StringCoding, encodeISOArray_name, indexOfI_signature, F_S) \ \ do_intrinsic(_encodeAsciiArray, java_lang_StringCoding, encodeAsciiArray_name, encodeISOArray_signature, F_S) \ - do_name( encodeAsciiArray_name, "implEncodeAsciiArray") \ + do_name( encodeAsciiArray_name, "encodeAsciiArray0") \ \ do_class(java_math_BigInteger, "java/math/BigInteger") \ do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_S) \ diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 5e75511b657..abd93fdd876 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -281,7 +281,7 @@ macro(OpaqueLoopInit) macro(OpaqueLoopStride) macro(OpaqueMultiversioning) macro(OpaqueZeroTripGuard) -macro(OpaqueNotNull) +macro(OpaqueConstantBool) macro(OpaqueInitializedAssertionPredicate) macro(OpaqueTemplateAssertionPredicate) macro(ProfileBoolean) diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index b46a12fcf89..5befdd924ff 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -588,7 +588,7 @@ bool ConnectionGraph::can_reduce_check_users(Node* n, uint nesting) const { // CmpP/N used by the If controlling the cast. if (use->in(0)->is_IfTrue() || use->in(0)->is_IfFalse()) { Node* iff = use->in(0)->in(0); - // We may have an OpaqueNotNull node between If and Bool nodes. But we could also have a sub class of IfNode, + // We may have an OpaqueConstantBool node between If and Bool nodes. But we could also have a sub class of IfNode, // for example, an OuterStripMinedLoopEnd or a Parse Predicate. Bail out in all these cases. bool can_reduce = (iff->Opcode() == Op_If) && iff->in(1)->is_Bool() && iff->in(1)->in(1)->is_Cmp(); if (can_reduce) { diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 3d127322439..084b137f313 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1472,7 +1472,7 @@ Node* GraphKit::cast_not_null(Node* obj, bool do_replace_in_map) { // In that case that data path will die and we need the control path // to become dead as well to keep the graph consistent. So we have to // add a check for null for which one branch can't be taken. It uses -// an OpaqueNotNull node that will cause the check to be removed after loop +// an OpaqueConstantBool node that will cause the check to be removed after loop // opts so the test goes away and the compiled code doesn't execute a // useless check. Node* GraphKit::must_be_not_null(Node* value, bool do_replace_in_map) { @@ -1481,7 +1481,7 @@ Node* GraphKit::must_be_not_null(Node* value, bool do_replace_in_map) { } Node* chk = _gvn.transform(new CmpPNode(value, null())); Node* tst = _gvn.transform(new BoolNode(chk, BoolTest::ne)); - Node* opaq = _gvn.transform(new OpaqueNotNullNode(C, tst)); + Node* opaq = _gvn.transform(new OpaqueConstantBoolNode(C, tst, true)); IfNode* iff = new IfNode(control(), opaq, PROB_MAX, COUNT_UNKNOWN); _gvn.set_type(iff, iff->Value(&_gvn)); if (!tst->is_Con()) { diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index e481833c816..b4e18b596e4 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -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 @@ -891,13 +891,16 @@ inline Node* LibraryCallKit::generate_fair_guard(Node* test, RegionNode* region) } inline Node* LibraryCallKit::generate_negative_guard(Node* index, RegionNode* region, - Node* *pos_index) { + Node** pos_index, bool with_opaque) { if (stopped()) return nullptr; // already stopped if (_gvn.type(index)->higher_equal(TypeInt::POS)) // [0,maxint] return nullptr; // index is already adequately typed Node* cmp_lt = _gvn.transform(new CmpINode(index, intcon(0))); Node* bol_lt = _gvn.transform(new BoolNode(cmp_lt, BoolTest::lt)); + if (with_opaque) { + bol_lt = _gvn.transform(new OpaqueConstantBoolNode(C, bol_lt, false)); + } Node* is_neg = generate_guard(bol_lt, region, PROB_MIN); if (is_neg != nullptr && pos_index != nullptr) { // Emulate effect of Parse::adjust_map_after_if. @@ -924,7 +927,8 @@ inline Node* LibraryCallKit::generate_negative_guard(Node* index, RegionNode* re inline Node* LibraryCallKit::generate_limit_guard(Node* offset, Node* subseq_length, Node* array_length, - RegionNode* region) { + RegionNode* region, + bool with_opaque) { if (stopped()) return nullptr; // already stopped bool zero_offset = _gvn.type(offset) == TypeInt::ZERO; @@ -935,12 +939,19 @@ inline Node* LibraryCallKit::generate_limit_guard(Node* offset, last = _gvn.transform(new AddINode(last, offset)); Node* cmp_lt = _gvn.transform(new CmpUNode(array_length, last)); Node* bol_lt = _gvn.transform(new BoolNode(cmp_lt, BoolTest::lt)); + if (with_opaque) { + bol_lt = _gvn.transform(new OpaqueConstantBoolNode(C, bol_lt, false)); + } Node* is_over = generate_guard(bol_lt, region, PROB_MIN); return is_over; } // Emit range checks for the given String.value byte array -void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count) { +void LibraryCallKit::generate_string_range_check(Node* array, + Node* offset, + Node* count, + bool char_count, + bool halt_on_oob) { if (stopped()) { return; // already stopped } @@ -952,16 +963,23 @@ void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node } // Offset and count must not be negative - generate_negative_guard(offset, bailout); - generate_negative_guard(count, bailout); + generate_negative_guard(offset, bailout, nullptr, halt_on_oob); + generate_negative_guard(count, bailout, nullptr, halt_on_oob); // Offset + count must not exceed length of array - generate_limit_guard(offset, count, load_array_length(array), bailout); + generate_limit_guard(offset, count, load_array_length(array), bailout, halt_on_oob); if (bailout->req() > 1) { - PreserveJVMState pjvms(this); - set_control(_gvn.transform(bailout)); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_maybe_recompile); + if (halt_on_oob) { + bailout = _gvn.transform(bailout)->as_Region(); + Node* frame = _gvn.transform(new ParmNode(C->start(), TypeFunc::FramePtr)); + Node* halt = _gvn.transform(new HaltNode(bailout, frame, "unexpected guard failure in intrinsic")); + C->root()->add_req(halt); + } else { + PreserveJVMState pjvms(this); + set_control(_gvn.transform(bailout)); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } } } @@ -1119,6 +1137,7 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) { //------------------------------inline_countPositives------------------------------ +// int java.lang.StringCoding#countPositives0(byte[] ba, int off, int len) bool LibraryCallKit::inline_countPositives() { if (too_many_traps(Deoptimization::Reason_intrinsic)) { return false; @@ -1131,12 +1150,11 @@ bool LibraryCallKit::inline_countPositives() { Node* len = argument(2); ba = must_be_not_null(ba, true); - - // Range checks - generate_string_range_check(ba, offset, len, false); + generate_string_range_check(ba, offset, len, false, true); if (stopped()) { return true; } + Node* ba_start = array_element_address(ba, offset, T_BYTE); Node* result = new CountPositivesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len); set_result(_gvn.transform(result)); @@ -6171,6 +6189,9 @@ CallStaticJavaNode* LibraryCallKit::get_uncommon_trap_from_success_proj(Node* no } //-------------inline_encodeISOArray----------------------------------- +// int sun.nio.cs.ISO_8859_1.Encoder#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// int java.lang.StringCoding#encodeISOArray0(byte[] sa, int sp, byte[] da, int dp, int len) +// int java.lang.StringCoding#encodeAsciiArray0(char[] sa, int sp, byte[] da, int dp, int len) // encode char[] to byte[] in ISO_8859_1 or ASCII bool LibraryCallKit::inline_encodeISOArray(bool ascii) { assert(callee()->signature()->size() == 5, "encodeISOArray has 5 parameters"); @@ -6181,8 +6202,12 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { Node *dst_offset = argument(3); Node *length = argument(4); + // Cast source & target arrays to not-null src = must_be_not_null(src, true); dst = must_be_not_null(dst, true); + if (stopped()) { + return true; + } const TypeAryPtr* src_type = src->Value(&_gvn)->isa_aryptr(); const TypeAryPtr* dst_type = dst->Value(&_gvn)->isa_aryptr(); @@ -6199,6 +6224,13 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) { return false; } + // Check source & target bounds + generate_string_range_check(src, src_offset, length, src_elem == T_BYTE, true); + generate_string_range_check(dst, dst_offset, length, false, true); + if (stopped()) { + return true; + } + Node* src_start = array_element_address(src, src_offset, T_CHAR); Node* dst_start = array_element_address(dst, dst_offset, dst_elem); // 'src_start' points to src array + scaled offset diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 00ba4c795f1..56141be2362 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.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 @@ -158,12 +158,15 @@ class LibraryCallKit : public GraphKit { Node* generate_fair_guard(Node* test, RegionNode* region); Node* generate_negative_guard(Node* index, RegionNode* region, // resulting CastII of index: - Node* *pos_index = nullptr); + Node** pos_index = nullptr, + bool with_opaque = false); Node* generate_limit_guard(Node* offset, Node* subseq_length, Node* array_length, - RegionNode* region); + RegionNode* region, + bool with_opaque = false); void generate_string_range_check(Node* array, Node* offset, - Node* length, bool char_count); + Node* length, bool char_count, + bool halt_on_oob = false); Node* current_thread_helper(Node* &tls_output, ByteSize handle_offset, bool is_immutable); Node* generate_current_thread(Node* &tls_output); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index fdb3ab89b82..4e221a9a0ef 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -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 @@ -1234,7 +1234,7 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop* phase, bool provisional, continue; } if (!bol->is_Bool()) { - assert(bol->is_OpaqueNotNull() || + assert(bol->is_OpaqueConstantBool() || bol->is_OpaqueTemplateAssertionPredicate() || bol->is_OpaqueInitializedAssertionPredicate() || bol->is_OpaqueMultiversioning(), diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 1855263539b..2b48780f9b0 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -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 @@ -1704,7 +1704,7 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) { !n->is_Proj() && !n->is_MergeMem() && !n->is_CMove() && - !n->is_OpaqueNotNull() && + !n->is_OpaqueConstantBool() && !n->is_OpaqueInitializedAssertionPredicate() && !n->is_OpaqueTemplateAssertionPredicate() && !is_raw_to_oop_cast && // don't extend live ranges of raw oops @@ -2045,14 +2045,14 @@ Node* PhaseIdealLoop::clone_iff(PhiNode* phi) { if (b->is_Phi()) { _igvn.replace_input_of(phi, i, clone_iff(b->as_Phi())); } else { - assert(b->is_Bool() || b->is_OpaqueNotNull() || b->is_OpaqueInitializedAssertionPredicate(), - "bool, non-null check with OpaqueNotNull or Initialized Assertion Predicate with its Opaque node"); + assert(b->is_Bool() || b->is_OpaqueConstantBool() || b->is_OpaqueInitializedAssertionPredicate(), + "bool, non-null check with OpaqueConstantBool or Initialized Assertion Predicate with its Opaque node"); } } Node* n = phi->in(1); Node* sample_opaque = nullptr; Node *sample_bool = nullptr; - if (n->is_OpaqueNotNull() || n->is_OpaqueInitializedAssertionPredicate()) { + if (n->is_OpaqueConstantBool() || n->is_OpaqueInitializedAssertionPredicate()) { sample_opaque = n; sample_bool = n->in(1); assert(sample_bool->is_Bool(), "wrong type"); @@ -2228,7 +2228,7 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new, // split if to break. assert(!use->is_OpaqueTemplateAssertionPredicate(), "should not clone a Template Assertion Predicate which should be removed once it's useless"); - if (use->is_If() || use->is_CMove() || use->is_OpaqueNotNull() || use->is_OpaqueInitializedAssertionPredicate() || + if (use->is_If() || use->is_CMove() || use->is_OpaqueConstantBool() || use->is_OpaqueInitializedAssertionPredicate() || (use->Opcode() == Op_AllocateArray && use->in(AllocateNode::ValidLengthTest) == old)) { // Since this code is highly unlikely, we lazily build the worklist // of such Nodes to go split. diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 9470001b2d2..ef38a511a88 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -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 @@ -2499,7 +2499,7 @@ void PhaseMacroExpand::eliminate_macro_nodes() { assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_ModD || n->Opcode() == Op_ModF || - n->is_OpaqueNotNull() || + n->is_OpaqueConstantBool() || n->is_OpaqueInitializedAssertionPredicate() || n->Opcode() == Op_MaxL || n->Opcode() == Op_MinL || @@ -2547,14 +2547,14 @@ void PhaseMacroExpand::eliminate_opaque_looplimit_macro_nodes() { } else if (n->is_Opaque1()) { _igvn.replace_node(n, n->in(1)); success = true; - } else if (n->is_OpaqueNotNull()) { - // Tests with OpaqueNotNull nodes are implicitly known to be true. Replace the node with true. In debug builds, + } else if (n->is_OpaqueConstantBool()) { + // Tests with OpaqueConstantBool nodes are implicitly known. Replace the node with true/false. In debug builds, // we leave the test in the graph to have an additional sanity check at runtime. If the test fails (i.e. a bug), // we will execute a Halt node. #ifdef ASSERT _igvn.replace_node(n, n->in(1)); #else - _igvn.replace_node(n, _igvn.intcon(1)); + _igvn.replace_node(n, _igvn.intcon(n->as_OpaqueConstantBool()->constant())); #endif success = true; } else if (n->is_OpaqueInitializedAssertionPredicate()) { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 5ddc4236b3e..e65578924d1 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -143,7 +143,7 @@ class Opaque1Node; class OpaqueLoopInitNode; class OpaqueLoopStrideNode; class OpaqueMultiversioningNode; -class OpaqueNotNullNode; +class OpaqueConstantBoolNode; class OpaqueInitializedAssertionPredicateNode; class OpaqueTemplateAssertionPredicateNode; class OuterStripMinedLoopNode; @@ -818,7 +818,7 @@ public: DEFINE_CLASS_ID(OpaqueLoopInit, Opaque1, 0) DEFINE_CLASS_ID(OpaqueLoopStride, Opaque1, 1) DEFINE_CLASS_ID(OpaqueMultiversioning, Opaque1, 2) - DEFINE_CLASS_ID(OpaqueNotNull, Node, 17) + DEFINE_CLASS_ID(OpaqueConstantBool, Node, 17) DEFINE_CLASS_ID(OpaqueInitializedAssertionPredicate, Node, 18) DEFINE_CLASS_ID(OpaqueTemplateAssertionPredicate, Node, 19) DEFINE_CLASS_ID(Move, Node, 20) @@ -1000,7 +1000,7 @@ public: DEFINE_CLASS_QUERY(NegV) DEFINE_CLASS_QUERY(NeverBranch) DEFINE_CLASS_QUERY(Opaque1) - DEFINE_CLASS_QUERY(OpaqueNotNull) + DEFINE_CLASS_QUERY(OpaqueConstantBool) DEFINE_CLASS_QUERY(OpaqueInitializedAssertionPredicate) DEFINE_CLASS_QUERY(OpaqueTemplateAssertionPredicate) DEFINE_CLASS_QUERY(OpaqueLoopInit) diff --git a/src/hotspot/share/opto/opaquenode.cpp b/src/hotspot/share/opto/opaquenode.cpp index 29d625db190..428379e84ae 100644 --- a/src/hotspot/share/opto/opaquenode.cpp +++ b/src/hotspot/share/opto/opaquenode.cpp @@ -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 @@ -108,10 +108,16 @@ void OpaqueMultiversioningNode::dump_spec(outputStream *st) const { } #endif -const Type* OpaqueNotNullNode::Value(PhaseGVN* phase) const { +const Type* OpaqueConstantBoolNode::Value(PhaseGVN* phase) const { return phase->type(in(1)); } +#ifndef PRODUCT +void OpaqueConstantBoolNode::dump_spec(outputStream *st) const { + st->print(_constant ? " #true" : " #false"); +} +#endif + OpaqueTemplateAssertionPredicateNode::OpaqueTemplateAssertionPredicateNode(BoolNode* bol, CountedLoopNode* loop_node) : Node(nullptr, bol), _loop_node(loop_node), diff --git a/src/hotspot/share/opto/opaquenode.hpp b/src/hotspot/share/opto/opaquenode.hpp index 9edfe2a7258..bb3da2aa65f 100644 --- a/src/hotspot/share/opto/opaquenode.hpp +++ b/src/hotspot/share/opto/opaquenode.hpp @@ -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 @@ -130,15 +130,26 @@ public: // This node is used in the context of intrinsics. We sometimes implicitly know that an object is non-null even though // the compiler cannot prove it. We therefore add a corresponding cast to propagate this implicit knowledge. However, // this cast could become top during optimizations (input to cast becomes null) and the data path is folded. To ensure -// that the control path is also properly folded, we insert an If node with a OpaqueNotNullNode as condition. During -// macro expansion, we replace the OpaqueNotNullNodes with true in product builds such that the actually unneeded checks -// are folded and do not end up in the emitted code. In debug builds, we keep the actual checks as additional -// verification code (i.e. removing OpaqueNotNullNodes and use the BoolNode inputs instead). For more details, also see -// GraphKit::must_be_not_null(). -class OpaqueNotNullNode : public Node { +// that the control path is also properly folded, we insert an If node with a OpaqueConstantBoolNode as condition. +// During macro expansion, we replace the OpaqueConstantBoolNodes with true in product builds such that the actually +// unneeded checks are folded and do not end up in the emitted code. In debug builds, we keep the actual checks as +// additional verification code (i.e. removing OpaqueConstantBoolNodes and use the BoolNode inputs instead). For more +// details, also see GraphKit::must_be_not_null(). +// Similarly, sometimes we know that a size or limit guard is checked (e.g. there is already a guard in the caller) but +// the compiler cannot prove it. We could in principle avoid adding a guard in the intrinsic but in some cases (e.g. +// when the input is a constant that breaks the guard and the caller guard is not inlined) the input of the intrinsic +// can become top and the data path is folded. To ensure that the control path is also properly folded, we insert an +// OpaqueConstantBoolNode before the If node in the guard. During macro expansion, we replace the OpaqueConstantBoolNode +// with false in product builds such that the actually unneeded guards are folded and do not end up in the emitted code. +// In debug builds, we keep the actual checks as additional verification code (i.e. removing OpaqueConstantBoolNodes and +// use the BoolNode inputs instead). +class OpaqueConstantBoolNode : public Node { + private: + const bool _constant; public: - OpaqueNotNullNode(Compile* C, Node* tst) : Node(nullptr, tst) { - init_class_id(Class_OpaqueNotNull); + OpaqueConstantBoolNode(Compile* C, Node* tst, bool constant) : Node(nullptr, tst), _constant(constant) { + assert(tst->is_Bool() || tst->is_Con(), "Test node must be a BoolNode or a constant"); + init_class_id(Class_OpaqueConstantBool); init_flags(Flag_is_macro); C->add_macro_node(this); } @@ -146,6 +157,9 @@ class OpaqueNotNullNode : public Node { virtual int Opcode() const; virtual const Type* Value(PhaseGVN* phase) const; virtual const Type* bottom_type() const { return TypeInt::BOOL; } + int constant() const { return _constant ? 1 : 0; } + virtual uint size_of() const { return sizeof(OpaqueConstantBoolNode); } + NOT_PRODUCT(void dump_spec(outputStream* st) const); }; // This node is used for Template Assertion Predicate BoolNodes. A Template Assertion Predicate is always removed diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index 425c0c9e99d..de9ddb60b0c 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -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 @@ -307,7 +307,7 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) assert( bol->is_Bool(), "" ); if (bol->outcnt() == 1) { Node* use = bol->unique_out(); - if (use->is_OpaqueNotNull() || use->is_OpaqueTemplateAssertionPredicate() || + if (use->is_OpaqueConstantBool() || use->is_OpaqueTemplateAssertionPredicate() || use->is_OpaqueInitializedAssertionPredicate()) { if (use->outcnt() == 1) { Node* iff = use->unique_out(); @@ -331,8 +331,8 @@ bool PhaseIdealLoop::clone_cmp_down(Node* n, const Node* blk1, const Node* blk2) // Recursively sink any BoolNode for (DUIterator j = bol->outs(); bol->has_out(j); j++) { Node* u = bol->out(j); - // Uses are either IfNodes, CMoves, OpaqueNotNull, or Opaque*AssertionPredicate - if (u->is_OpaqueNotNull() || u->is_OpaqueTemplateAssertionPredicate() || + // Uses are either IfNodes, CMoves, OpaqueConstantBool or Opaque*AssertionPredicate + if (u->is_OpaqueConstantBool() || u->is_OpaqueTemplateAssertionPredicate() || u->is_OpaqueInitializedAssertionPredicate()) { assert(u->in(1) == bol, "bad input"); for (DUIterator_Last kmin, k = u->last_outs(kmin); k >= kmin; --k) { diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 1ac15e3a8b2..fc05febdb45 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -1111,7 +1111,7 @@ public final class String int sp = 0; int sl = len; while (sp < sl) { - int ret = StringCoding.implEncodeISOArray(val, sp, dst, dp, len); + int ret = StringCoding.encodeISOArray(val, sp, dst, dp, len); sp = sp + ret; dp = dp + ret; if (ret != len) { diff --git a/src/java.base/share/classes/java/lang/StringCoding.java b/src/java.base/share/classes/java/lang/StringCoding.java index c02af28c37d..3145e7d0216 100644 --- a/src/java.base/share/classes/java/lang/StringCoding.java +++ b/src/java.base/share/classes/java/lang/StringCoding.java @@ -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) 2024, Alibaba Group Holding Limited. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,8 +26,11 @@ package java.lang; +import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; +import java.util.function.BiFunction; + /** * Utility class for string encoding and decoding. */ @@ -38,7 +41,7 @@ class StringCoding { /** * Count the number of leading non-zero ascii chars in the range. */ - public static int countNonZeroAscii(String s) { + static int countNonZeroAscii(String s) { byte[] value = s.value(); if (s.isLatin1()) { return countNonZeroAsciiLatin1(value, 0, value.length); @@ -50,7 +53,7 @@ class StringCoding { /** * Count the number of non-zero ascii chars in the range. */ - public static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { + private static int countNonZeroAsciiLatin1(byte[] ba, int off, int len) { int limit = off + len; for (int i = off; i < limit; i++) { if (ba[i] <= 0) { @@ -63,7 +66,7 @@ class StringCoding { /** * Count the number of leading non-zero ascii chars in the range. */ - public static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { + private static int countNonZeroAsciiUTF16(byte[] ba, int off, int strlen) { int limit = off + strlen; for (int i = off; i < limit; i++) { char c = StringUTF16.charAt(ba, i); @@ -74,7 +77,7 @@ class StringCoding { return strlen; } - public static boolean hasNegatives(byte[] ba, int off, int len) { + static boolean hasNegatives(byte[] ba, int off, int len) { return countPositives(ba, off, len) != len; } @@ -85,9 +88,24 @@ class StringCoding { * bytes in the range. If there are negative bytes, the implementation must return * a value that is less than or equal to the index of the first negative byte * in the range. + * + * @param ba a byte array + * @param off the index of the first byte to start reading from + * @param len the total number of bytes to read + * @throws NullPointerException if {@code ba} is null + * @throws ArrayIndexOutOfBoundsException if the provided sub-range is + * {@linkplain Preconditions#checkFromIndexSize(int, int, int, BiFunction) out of bounds} */ + static int countPositives(byte[] ba, int off, int len) { + Preconditions.checkFromIndexSize( + off, len, + ba.length, // Implicit null check on `ba` + Preconditions.AIOOBE_FORMATTER); + return countPositives0(ba, off, len); + } + @IntrinsicCandidate - public static int countPositives(byte[] ba, int off, int len) { + private static int countPositives0(byte[] ba, int off, int len) { int limit = off + len; for (int i = off; i < limit; i++) { if (ba[i] < 0) { @@ -97,9 +115,37 @@ class StringCoding { return len; } + /** + * Encodes as many ISO-8859-1 codepoints as possible from the source byte + * array containing characters encoded in UTF-16, into the destination byte + * array, assuming that the encoding is ISO-8859-1 compatible. + * + * @param sa the source byte array containing characters encoded in UTF-16 + * @param sp the index of the character (not byte!) from the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters (not bytes!) to be encoded + * @return the total number of characters (not bytes!) successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ + static int encodeISOArray(byte[] sa, int sp, + byte[] da, int dp, int len) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + int sl; + if ((sp | dp | len) < 0 || + // Halving the length of `sa` to obtain the number of characters: + sp >= (sl = sa.length >>> 1) || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` + return 0; + } + int minLen = Math.min(len, Math.min(sl - sp, da.length - dp)); + return encodeISOArray0(sa, sp, da, dp, minLen); + } + @IntrinsicCandidate - public static int implEncodeISOArray(byte[] sa, int sp, - byte[] da, int dp, int len) { + private static int encodeISOArray0(byte[] sa, int sp, + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = StringUTF16.getChar(sa, sp++); @@ -110,10 +156,35 @@ class StringCoding { return i; } + /** + * Encodes as many ASCII codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ASCII compatible. + * + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ + static int encodeAsciiArray(char[] sa, int sp, + byte[] da, int dp, int len) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + if ((sp | dp | len) < 0 || + sp >= sa.length || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` + return 0; + } + int minLen = Math.min(len, Math.min(sa.length - sp, da.length - dp)); + return encodeAsciiArray0(sa, sp, da, dp, minLen); + } + @IntrinsicCandidate - public static int implEncodeAsciiArray(char[] sa, int sp, - byte[] da, int dp, int len) - { + static int encodeAsciiArray0(char[] sa, int sp, + byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = sa[sp++]; diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 831ce81c7c5..bd684fab629 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2176,8 +2176,8 @@ public final class System { return String.decodeASCII(src, srcOff, dst, dstOff, len); } - public int uncheckedEncodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len) { - return StringCoding.implEncodeAsciiArray(src, srcOff, dst, dstOff, len); + public int encodeASCII(char[] sa, int sp, byte[] da, int dp, int len) { + return StringCoding.encodeAsciiArray(sa, sp, da, dp, len); } public InputStream initialSystemIn() { diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index e578bb2f6ff..4ae1905aa10 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -448,15 +448,19 @@ public interface JavaLangAccess { PrintStream initialSystemErr(); /** - * Encodes as many ASCII codepoints as possible from the source array into - * the destination byte array, assuming that the encoding is ASCII - * compatible. - *

      - * WARNING: This method does not perform any bound checks. + * Encodes as many ASCII codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ASCII compatible. * - * @return the number of bytes successfully encoded, or 0 if none + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the total number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null */ - int uncheckedEncodeASCII(char[] src, int srcOff, byte[] dst, int dstOff, int len); + int encodeASCII(char[] sa, int sp, byte[] da, int dp, int len); /** * Set the cause of Throwable diff --git a/src/java.base/share/classes/sun/nio/cs/CESU_8.java b/src/java.base/share/classes/sun/nio/cs/CESU_8.java index 11f21f56139..7093e0fecb2 100644 --- a/src/java.base/share/classes/sun/nio/cs/CESU_8.java +++ b/src/java.base/share/classes/sun/nio/cs/CESU_8.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 @@ -445,7 +445,7 @@ class CESU_8 extends Unicode int dl = dst.arrayOffset() + dst.limit(); // Handle ASCII-only prefix - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java index 0aaae14bbf9..edad4a1f67b 100644 --- a/src/java.base/share/classes/sun/nio/cs/DoubleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/DoubleByte.java @@ -610,7 +610,7 @@ public class DoubleByte { try { if (isASCIICompatible) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); sp += n; dp += n; } diff --git a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java index 9240ac3f380..036aaece9ab 100644 --- a/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java +++ b/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java @@ -35,7 +35,6 @@ import java.util.Objects; import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; -import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.IntrinsicCandidate; public class ISO_8859_1 @@ -152,20 +151,34 @@ public class ISO_8859_1 private final Surrogate.Parser sgp = new Surrogate.Parser(); - // Method possible replaced with a compiler intrinsic. + /** + * Encodes as many ISO-8859-1 codepoints as possible from the source + * character array into the destination byte array, assuming that + * the encoding is ISO-8859-1 compatible. + * + * @param sa the source character array + * @param sp the index of the source array to start reading from + * @param da the target byte array + * @param dp the index of the target array to start writing to + * @param len the maximum number of characters to be encoded + * @return the total number of characters successfully encoded + * @throws NullPointerException if any of the provided arrays is null + */ private static int encodeISOArray(char[] sa, int sp, byte[] da, int dp, int len) { - if (len <= 0) { + // This method should tolerate invalid arguments, matching the lenient behavior of the VM intrinsic. + // Hence, using operator expressions instead of `Preconditions`, which throw on failure. + if ((sp | dp | len) < 0 || + sp >= sa.length || // Implicit null check on `sa` + dp >= da.length) { // Implicit null check on `da` return 0; } - encodeISOArrayCheck(sa, sp, da, dp, len); - return implEncodeISOArray(sa, sp, da, dp, len); + int minLen = Math.min(len, Math.min(sa.length - sp, da.length - dp)); + return encodeISOArray0(sa, sp, da, dp, minLen); } @IntrinsicCandidate - private static int implEncodeISOArray(char[] sa, int sp, - byte[] da, int dp, int len) - { + private static int encodeISOArray0(char[] sa, int sp, byte[] da, int dp, int len) { int i = 0; for (; i < len; i++) { char c = sa[sp++]; @@ -176,17 +189,6 @@ public class ISO_8859_1 return i; } - private static void encodeISOArrayCheck(char[] sa, int sp, - byte[] da, int dp, int len) { - Objects.requireNonNull(sa); - Objects.requireNonNull(da); - Preconditions.checkIndex(sp, sa.length, Preconditions.AIOOBE_FORMATTER); - Preconditions.checkIndex(dp, da.length, Preconditions.AIOOBE_FORMATTER); - - Preconditions.checkIndex(sp + len - 1, sa.length, Preconditions.AIOOBE_FORMATTER); - Preconditions.checkIndex(dp + len - 1, da.length, Preconditions.AIOOBE_FORMATTER); - } - private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { diff --git a/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/src/java.base/share/classes/sun/nio/cs/SingleByte.java index d4127b7c043..dad89b4ebd3 100644 --- a/src/java.base/share/classes/sun/nio/cs/SingleByte.java +++ b/src/java.base/share/classes/sun/nio/cs/SingleByte.java @@ -227,7 +227,7 @@ public class SingleByte int len = Math.min(dl - dp, sl - sp); if (isASCIICompatible) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, len); + int n = JLA.encodeASCII(sa, sp, da, dp, len); sp += n; dp += n; len -= n; diff --git a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java index 61c4948e949..dabe843c809 100644 --- a/src/java.base/share/classes/sun/nio/cs/US_ASCII.java +++ b/src/java.base/share/classes/sun/nio/cs/US_ASCII.java @@ -169,7 +169,7 @@ public class US_ASCII assert (dp <= dl); dp = (dp <= dl ? dp : dl); - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/src/java.base/share/classes/sun/nio/cs/UTF_8.java index 54e479f838a..2928ae6d509 100644 --- a/src/java.base/share/classes/sun/nio/cs/UTF_8.java +++ b/src/java.base/share/classes/sun/nio/cs/UTF_8.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 @@ -452,7 +452,7 @@ public final class UTF_8 extends Unicode { int dl = dst.arrayOffset() + dst.limit(); // Handle ASCII-only prefix - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(sl - sp, dl - dp)); sp += n; dp += n; diff --git a/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template b/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template index 4fc0b2796ee..e4eb2feab9d 100644 --- a/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template +++ b/src/jdk.charsets/share/classes/sun/nio/cs/ext/EUC_JP.java.template @@ -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 @@ -309,7 +309,7 @@ public class EUC_JP try { if (enc0201.isASCIICompatible()) { - int n = JLA.uncheckedEncodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); + int n = JLA.encodeASCII(sa, sp, da, dp, Math.min(dl - dp, sl - sp)); sp += n; dp += n; } diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestCanReduceCheckUsersDifferentIfs.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestCanReduceCheckUsersDifferentIfs.java index b71f9b2cef2..9e2d3eeb752 100644 --- a/test/hotspot/jtreg/compiler/escapeAnalysis/TestCanReduceCheckUsersDifferentIfs.java +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestCanReduceCheckUsersDifferentIfs.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 @@ -64,7 +64,7 @@ public class TestCanReduceCheckUsersDifferentIfs { // (6) CastPP(phi1) ends up at IfFalse of OuterStripMinedLoopEnd of loop (L). // (7) EA tries to reduce phi1(CheckCastPP(B), CheckCastPP(c)) and looks at // OuterStripMinedLoopEnd and asserts that if it's not an IfNode that it has - // an OpaqueNotNull which obviously is not the case and the assert fails. + // an OpaqueConstantBool which obviously is not the case and the assert fails. // (5) Found to be false after PhaseIdealLoop before EA and is folded away. if (y == 76) { @@ -77,7 +77,7 @@ public class TestCanReduceCheckUsersDifferentIfs { } // Same as testOuterStripMinedLoopEnd() but we find in (7) a ParsePredicate from the - // removed loop (L) which also does not have an OpaqueNotNull and the assert fails. + // removed loop (L) which also does not have an OpaqueConstantBool and the assert fails. static void testParsePredicate() { A a = flag ? new B() : new C(); diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java index 76ef4766159..94136c71c53 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestCountPositives.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java index 38a516e7521..0165a77d803 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2021, 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 diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java index 6edf2dc2e56..f80b7f63402 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestHasNegatives.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 diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java new file mode 100644 index 00000000000..66f3da23abe --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java @@ -0,0 +1,60 @@ +/* + * 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 8374582 + * @summary Tests the creation and removal of opaque nodes at range checks points in string intrinsics. + * @requires vm.flagless + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.intrinsics.string; + +import compiler.lib.ir_framework.*; + +public class TestOpaqueConstantBoolNodes { + + static byte[] bytes = new byte[42]; + + public static void main(String[] args) { + TestFramework.runWithFlags( + "-XX:CompileCommand=inline,java.lang.StringCoding::*", + "-XX:CompileCommand=exclude,jdk.internal.util.Preconditions::checkFromIndexSize"); + } + + @Setup + private static Object[] setup() { + return new Object[] {bytes, 2, 23}; + } + + @Test + @IR(counts = {IRNode.OPAQUE_CONSTANT_BOOL, "3"}, phase = CompilePhase.AFTER_PARSING) + @IR(failOn = {IRNode.OPAQUE_CONSTANT_BOOL}, phase = CompilePhase.AFTER_MACRO_EXPANSION) + @Arguments(setup = "setup") + private static String test(byte[] bytes, int i, int l) { + return new String(bytes, i , l); + } +} + diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestRangeCheck.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestRangeCheck.java new file mode 100644 index 00000000000..3b01e0f85a4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestRangeCheck.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. + */ + +/* + * @test + * @bug 8374582 + * @summary Tests handling of invalid array indices in C2 intrinsic if explicit range check in Java code is not inlined. + * @modules java.base/jdk.internal.access + * @run main/othervm + * -XX:CompileCommand=inline,java.lang.StringCoding::* + * -XX:CompileCommand=exclude,jdk.internal.util.Preconditions::checkFromIndexSize + * ${test.main.class} + */ + +package compiler.intrinsics.string; + +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; + +public class TestRangeCheck { + + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + + public static void main(String[] args) { + byte[] bytes = new byte[42]; + for (int i = 0; i < 10_000; ++i) { + test(bytes); + } + } + + private static int test(byte[] bytes) { + try { + // Calling `StringCoding::countPositives`, which is a "front door" + // to the `StringCoding::countPositives0` intrinsic. + // `countPositives` validates its input using + // `Preconditions::checkFromIndexSize`, which also maps to an + // intrinsic. When `checkFromIndexSize` is not inlined, C2 does not + // know about the explicit range checks, and does not cut off the + // dead code. As a result, an invalid value (e.g., `-1`) can be fed + // as input into the `countPositives0` intrinsic, get replaced + // by TOP, and cause a failure in the matcher. + return JLA.countPositives(bytes, -1, 42); + } catch (Exception e) { + return 0; + } + } +} + diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 16f4b7847a2..43cae5aa6c7 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -3147,6 +3147,11 @@ public class IRNode { machOnlyNameRegex(REPLICATE_HF_IMM8, "replicateHF_imm8_gt128b"); } + public static final String OPAQUE_CONSTANT_BOOL = PREFIX + "OPAQUE_CONSTANT_BOOL" + POSTFIX; + static { + beforeMatchingNameRegex(OPAQUE_CONSTANT_BOOL, "OpaqueConstantBool"); + } + /* * Utility methods to set up IR_NODE_MAPPINGS. */ diff --git a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java index e6c8b68fc6f..19eb98af3ba 100644 --- a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java +++ b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.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 diff --git a/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java b/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java index 7977125b9cf..9a31cbbb057 100644 --- a/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java +++ b/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.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 @@ -65,15 +65,15 @@ public class OpaqueAccesses { // Finish the line after the node type, skips full line, and eats until before the node types private static final String SKIP = IRNode.MID + IRNode.END + "\\R" + FULL_LINES + "\\s*" + IRNode.START; - private static final String CALL_STATIC_JAVA_AND_THEN_OPAQUE_NOT_NULL = IRNode.START + "CallStaticJava" + SKIP + "OpaqueNotNull" + IRNode.MID + IRNode.END; - private static final String OPAQUE_NOT_NULL_AND_THEN_CALL_STATIC_JAVA = IRNode.START + "OpaqueNotNull" + SKIP + "CallStaticJava" + IRNode.MID + IRNode.END; - /* Having both CallStaticJava and OpaqueNotNull, in any order. We use that in a failOn to make sure we have one + private static final String CALL_STATIC_JAVA_AND_THEN_OPAQUE_CONSTANT_BOOL = IRNode.START + "CallStaticJava" + SKIP + "OpaqueConstantBool" + IRNode.MID + IRNode.END; + private static final String OPAQUE_CONSTANT_BOOL_AND_THEN_CALL_STATIC_JAVA = IRNode.START + "OpaqueConstantBool" + SKIP + "CallStaticJava" + IRNode.MID + IRNode.END; + /* Having both CallStaticJava and OpaqueConstantBool, in any order. We use that in a failOn to make sure we have one * or the other (or none), but not both. - * The CallStaticJava happens when the call is not intrinsified, and the OpaqueNotNull comes from the intrinsic. + * The CallStaticJava happens when the call is not intrinsified, and the OpaqueConstantBool comes from the intrinsic. * We don't want a unfinished intrinsic, with the call nevertheless. */ private static final String BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL = - "(" + CALL_STATIC_JAVA_AND_THEN_OPAQUE_NOT_NULL + ") | (" + OPAQUE_NOT_NULL_AND_THEN_CALL_STATIC_JAVA + ")"; + "(" + CALL_STATIC_JAVA_AND_THEN_OPAQUE_CONSTANT_BOOL + ") | (" + OPAQUE_CONSTANT_BOOL_AND_THEN_CALL_STATIC_JAVA + ")"; @Test From 1efb29829fdd526be55c0a00420980279d9824ee Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Fri, 6 Feb 2026 09:30:38 +0000 Subject: [PATCH 149/215] 8375598: VM crashes with "assert((labs(val) & 0xFFFFFFFF00000000) == 0 || dest == (address)-1) failed: must be 32bit offset or -1" when using too high value for NonNMethodCodeHeapSize Reviewed-by: mdoerr, kvn --- src/hotspot/share/code/codeCache.cpp | 80 ++++++++++++++----- .../codecache/CheckSegmentedCodeCache.java | 11 ++- 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 95a2fb908de..481eb51bd5c 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -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 @@ -248,13 +248,61 @@ void CodeCache::initialize_heaps() { set_size_of_unset_code_heap(&non_nmethod, cache_size, profiled.size + non_profiled.size, non_nmethod_min_size); } - size_t total = non_nmethod.size + profiled.size + non_profiled.size; - if (total != cache_size && !cache_size_set) { - log_info(codecache)("ReservedCodeCache size %zuK changed to total segments size NonNMethod " - "%zuK NonProfiled %zuK Profiled %zuK = %zuK", - cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, total/K); - // Adjust ReservedCodeCacheSize as necessary because it was not set explicitly - cache_size = total; + // Note: if large page support is enabled, min_size is at least the large + // page size. This ensures that the code cache is covered by large pages. + non_nmethod.size = align_up(non_nmethod.size, min_size); + profiled.size = align_up(profiled.size, min_size); + non_profiled.size = align_up(non_profiled.size, min_size); + + size_t aligned_total = non_nmethod.size + profiled.size + non_profiled.size; + if (!cache_size_set) { + // If ReservedCodeCacheSize is explicitly set and exceeds CODE_CACHE_SIZE_LIMIT, + // it is rejected by flag validation elsewhere. Here we only handle the case + // where ReservedCodeCacheSize is not set explicitly, but the computed segmented + // sizes (after alignment) exceed the platform limit. + if (aligned_total > CODE_CACHE_SIZE_LIMIT) { + err_msg message("ReservedCodeCacheSize (%zuK), Max (%zuK)." + "Segments: NonNMethod (%zuK), NonProfiled (%zuK), Profiled (%zuK).", + aligned_total/K, CODE_CACHE_SIZE_LIMIT/K, + non_nmethod.size/K, non_profiled.size/K, profiled.size/K); + vm_exit_during_initialization("Code cache size exceeds platform limit", message); + } + if (aligned_total != cache_size) { + log_info(codecache)("ReservedCodeCache size %zuK changed to total segments size NonNMethod " + "%zuK NonProfiled %zuK Profiled %zuK = %zuK", + cache_size/K, non_nmethod.size/K, non_profiled.size/K, profiled.size/K, aligned_total/K); + // Adjust ReservedCodeCacheSize as necessary because it was not set explicitly + cache_size = aligned_total; + } + } else { + check_min_size("reserved code cache", cache_size, min_cache_size); + // ReservedCodeCacheSize was set explicitly, so treat it as a hard cap. + // If alignment causes the total to exceed the cap, shrink unset heaps + // in min_size steps, never below their minimum sizes. + // + // A total smaller than cache_size typically happens when all segment sizes + // are explicitly set. In that case there is nothing to adjust, so we + // only validate the sizes. + if (aligned_total > cache_size) { + size_t delta = (aligned_total - cache_size) / min_size; + while (delta > 0) { + size_t start_delta = delta; + // Do not shrink the non-nmethod heap here: running out of non-nmethod space + // is more critical and may lead to unrecoverable VM errors. + if (non_profiled.enabled && !non_profiled.set && non_profiled.size > min_size) { + non_profiled.size -= min_size; + if (--delta == 0) break; + } + if (profiled.enabled && !profiled.set && profiled.size > min_size) { + profiled.size -= min_size; + delta--; + } + if (delta == start_delta) { + break; + } + } + aligned_total = non_nmethod.size + profiled.size + non_profiled.size; + } } log_debug(codecache)("Initializing code heaps ReservedCodeCache %zuK NonNMethod %zuK" @@ -270,12 +318,9 @@ void CodeCache::initialize_heaps() { if (non_profiled.enabled) { // non_profiled.enabled is always ON for segmented code heap, leave it checked for clarity check_min_size("non-profiled code heap", non_profiled.size, min_size); } - if (cache_size_set) { - check_min_size("reserved code cache", cache_size, min_cache_size); - } // ReservedCodeCacheSize was set explicitly, so report an error and abort if it doesn't match the segment sizes - if (total != cache_size && cache_size_set) { + if (aligned_total != cache_size && cache_size_set) { err_msg message("NonNMethodCodeHeapSize (%zuK)", non_nmethod.size/K); if (profiled.enabled) { message.append(" + ProfiledCodeHeapSize (%zuK)", profiled.size/K); @@ -283,8 +328,8 @@ void CodeCache::initialize_heaps() { if (non_profiled.enabled) { message.append(" + NonProfiledCodeHeapSize (%zuK)", non_profiled.size/K); } - message.append(" = %zuK", total/K); - message.append((total > cache_size) ? " is greater than " : " is less than "); + message.append(" = %zuK", aligned_total/K); + message.append((aligned_total > cache_size) ? " is greater than " : " is less than "); message.append("ReservedCodeCacheSize (%zuK).", cache_size/K); vm_exit_during_initialization("Invalid code heap sizes", message); @@ -300,13 +345,6 @@ void CodeCache::initialize_heaps() { } } - // Note: if large page support is enabled, min_size is at least the large - // page size. This ensures that the code cache is covered by large pages. - non_nmethod.size = align_up(non_nmethod.size, min_size); - profiled.size = align_up(profiled.size, min_size); - non_profiled.size = align_up(non_profiled.size, min_size); - cache_size = non_nmethod.size + profiled.size + non_profiled.size; - FLAG_SET_ERGO(NonNMethodCodeHeapSize, non_nmethod.size); FLAG_SET_ERGO(ProfiledCodeHeapSize, profiled.size); FLAG_SET_ERGO(NonProfiledCodeHeapSize, non_profiled.size); diff --git a/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java b/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java index 06b51b1641d..a99f6b83cc6 100644 --- a/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java +++ b/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.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,7 +23,7 @@ /* * @test CheckSegmentedCodeCache - * @bug 8015774 + * @bug 8015774 8375598 * @summary Checks VM options related to the segmented code cache * @library /test/lib * @requires vm.flagless @@ -247,5 +247,12 @@ public class CheckSegmentedCodeCache { "-XX:+PrintFlagsFinal", "-version"); verifyCodeHeapSize(pb, "NonNMethodCodeHeapSize", 83886080); + + // A large NonNMethodCodeHeapSize can make the total code cache exceed the platform limit. + // The VM should fail fast during initialization. + pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+SegmentedCodeCache", + "-XX:NonNMethodCodeHeapSize=3G", + "-version"); + failsWith(pb, "Code cache size exceeds platform limit"); } } From 2d6a1aa36e9a247d41a07817d61236a519879762 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 6 Feb 2026 09:59:02 +0000 Subject: [PATCH 150/215] 8377313: java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java#id0 timeout Reviewed-by: jpai --- .../virtual/stress/GetStackTraceALotWhenBlocking.java | 6 +++--- .../Thread/virtual/stress/GetStackTraceALotWhenPinned.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java index 733ee261b09..15a134f0bb2 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.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 @@ -28,7 +28,7 @@ * @requires vm.debug != true * @modules jdk.management * @library /test/lib - * @run main/othervm/timeout=1200 GetStackTraceALotWhenBlocking 100000 + * @run main/othervm/timeout=1200 GetStackTraceALotWhenBlocking 10000 */ /* @@ -36,7 +36,7 @@ * @requires vm.debug == true & vm.continuations * @modules jdk.management * @library /test/lib - * @run main/othervm/timeout=1200 GetStackTraceALotWhenBlocking 50000 + * @run main/othervm/timeout=1200 GetStackTraceALotWhenBlocking 5000 */ import java.time.Instant; diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java index ef385e47e21..a23a0de36d2 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.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 @@ -28,7 +28,7 @@ * @requires vm.debug != true * @modules jdk.management * @library /test/lib - * @run main/othervm/native/timeout=300 --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 100000 + * @run main/othervm/native/timeout=300 --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 10000 */ /* @@ -36,7 +36,7 @@ * @requires vm.debug == true * @modules jdk.management * @library /test/lib - * @run main/othervm/native/timeout=300 --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 50000 + * @run main/othervm/native/timeout=300 --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 5000 */ import java.time.Instant; From 5f83e9ad0e57396b58520f2bb1dfb3e10c7113b3 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Fri, 6 Feb 2026 10:36:03 +0000 Subject: [PATCH 151/215] 8377179: Improve and document racy use of start/end in ThreadLocalAllocBuffer Reviewed-by: iwalulya, ayang --- .../gc/shared/threadLocalAllocBuffer.cpp | 23 ++++++++---- .../gc/shared/threadLocalAllocBuffer.hpp | 36 +++++++------------ src/hotspot/share/runtime/thread.inline.hpp | 25 ++++--------- 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index 9635ed4d0cb..e86881d3523 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -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 @@ -458,10 +458,19 @@ size_t ThreadLocalAllocBuffer::end_reserve() { return MAX2(reserve_size, (size_t)_reserve_for_allocation_prefetch); } -const HeapWord* ThreadLocalAllocBuffer::start_relaxed() const { - return AtomicAccess::load(&_start); -} - -const HeapWord* ThreadLocalAllocBuffer::top_relaxed() const { - return AtomicAccess::load(&_top); +size_t ThreadLocalAllocBuffer::estimated_used_bytes() const { + HeapWord* start = AtomicAccess::load(&_start); + HeapWord* top = AtomicAccess::load(&_top); + // There has been a race when retrieving _top and _start. Return 0. + if (_top < _start) { + return 0; + } + size_t used_bytes = pointer_delta(_top, _start, 1); + // Comparing diff with the maximum allowed size will ensure that we don't add + // the used bytes from a semi-initialized TLAB ending up with implausible values. + // In this case also just return 0. + if (used_bytes > ThreadLocalAllocBuffer::max_size_in_bytes()) { + return 0; + } + return used_bytes; } diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index 8267a103539..a50e7c9533c 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.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. * 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,16 +33,13 @@ class ThreadLocalAllocStats; // ThreadLocalAllocBuffer: a descriptor for thread-local storage used by -// the threads for allocation. -// It is thread-private at any time, but maybe multiplexed over -// time across multiple threads. The park()/unpark() pair is -// used to make it available for such multiplexing. +// the threads for allocation. It is thread-private at any time. // -// Heap sampling is performed via the end and allocation_end -// fields. -// allocation_end contains the real end of the tlab allocation, -// whereas end can be set to an arbitrary spot in the tlab to -// trip the return and sample the allocation. +// Heap sampling is performed via the end and allocation_end +// fields. +// allocation_end contains the real end of the tlab allocation, +// whereas end can be set to an arbitrary spot in the tlab to +// trip the return and sample the allocation. class ThreadLocalAllocBuffer: public CHeapObj { friend class VMStructs; friend class JVMCIVMStructs; @@ -116,17 +113,18 @@ public: HeapWord* end() const { return _end; } HeapWord* top() const { return _top; } HeapWord* hard_end(); - HeapWord* pf_top() const { return _pf_top; } size_t desired_size() const { return _desired_size; } - size_t used() const { return pointer_delta(top(), start()); } size_t used_bytes() const { return pointer_delta(top(), start(), 1); } size_t free() const { return pointer_delta(end(), top()); } // Don't discard tlab if remaining space is larger than this. size_t refill_waste_limit() const { return _refill_waste_limit; } - // For external inspection. - const HeapWord* start_relaxed() const; - const HeapWord* top_relaxed() const; + // Returns an estimate of the number of bytes currently used in the TLAB. + // Due to races with concurrent allocations and/or resetting the TLAB the return + // value may be inconsistent with any other metrics (e.g. total allocated + // bytes), and may just incorrectly return 0. + // Intented fo external inspection only where accuracy is not 100% required. + size_t estimated_used_bytes() const; // Allocate size HeapWords. The memory is NOT initialized to zero. inline HeapWord* allocate(size_t size); @@ -171,14 +169,6 @@ public: static size_t refill_waste_limit_increment(); - template void addresses_do(T f) { - f(&_start); - f(&_top); - f(&_pf_top); - f(&_end); - f(&_allocation_end); - } - // Code generation support static ByteSize start_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _start); } static ByteSize end_offset() { return byte_offset_of(ThreadLocalAllocBuffer, _end); } diff --git a/src/hotspot/share/runtime/thread.inline.hpp b/src/hotspot/share/runtime/thread.inline.hpp index 8e80cfc6125..61866674224 100644 --- a/src/hotspot/share/runtime/thread.inline.hpp +++ b/src/hotspot/share/runtime/thread.inline.hpp @@ -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. * Copyright (c) 2021, Azul Systems, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -37,25 +37,14 @@ inline jlong Thread::cooked_allocated_bytes() { jlong allocated_bytes = AtomicAccess::load_acquire(&_allocated_bytes); + size_t used_bytes = 0; if (UseTLAB) { - // These reads are unsynchronized and unordered with the thread updating its tlab pointers. - // Use only if top > start && used_bytes <= max_tlab_size_bytes. - const HeapWord* const top = tlab().top_relaxed(); - const HeapWord* const start = tlab().start_relaxed(); - if (top <= start) { - return allocated_bytes; - } - const size_t used_bytes = pointer_delta(top, start, 1); - if (used_bytes <= ThreadLocalAllocBuffer::max_size_in_bytes()) { - // Comparing used_bytes with the maximum allowed size will ensure - // that we don't add the used bytes from a semi-initialized TLAB - // ending up with incorrect values. There is still a race between - // incrementing _allocated_bytes and clearing the TLAB, that might - // cause double counting in rare cases. - return allocated_bytes + used_bytes; - } + // cooked_used_bytes() does its best to not return implausible values, but + // there is still a potential race between incrementing _allocated_bytes and + // clearing the TLAB, that might cause double-counting. + used_bytes = tlab().estimated_used_bytes(); } - return allocated_bytes; + return allocated_bytes + used_bytes; } inline ThreadsList* Thread::cmpxchg_threads_hazard_ptr(ThreadsList* exchange_value, ThreadsList* compare_value) { From cd6a7a54c5e323ec53747f76b07edb7f90e1f965 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 6 Feb 2026 10:42:52 +0000 Subject: [PATCH 152/215] 8377245: AbstractMemorySegmentImpl#getString with length should be @ForceInline Reviewed-by: mcimadamore --- .../classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index f75d67adbbb..235797f66e3 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -551,6 +551,7 @@ public abstract sealed class AbstractMemorySegmentImpl unsafeGetOffset() == that.unsafeGetOffset(); } + @ForceInline @Override public String getString(long offset, Charset charset, long byteLength) { Utils.checkNonNegativeArgument(byteLength, "byteLength"); From 7a37d370e3fe0a2adb4c6ae336803b87be8d8547 Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Fri, 6 Feb 2026 11:01:18 +0000 Subject: [PATCH 153/215] 8377326: [PPC64] build without C1 and C2 broken Reviewed-by: dbriemann, mbaesken --- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 4eb2028f529..5260ed978ff 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2025 SAP SE. All rights reserved. + * Copyright (c) 2012, 2026 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 @@ -775,7 +775,6 @@ int SharedRuntime::java_calling_convention(const BasicType *sig_bt, return stk; } -#if defined(COMPILER1) || defined(COMPILER2) // Calling convention for calling C code. int SharedRuntime::c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, @@ -913,7 +912,6 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, return MAX2(arg, 8) * 2 + additional_frame_header_slots; #endif } -#endif // COMPILER2 int SharedRuntime::vector_calling_convention(VMRegPair *regs, uint num_bits, @@ -2874,7 +2872,6 @@ void SharedRuntime::generate_deopt_blob() { CodeBuffer buffer(name, 2048, 1024); InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); Label exec_mode_initialized; - int frame_size_in_words; OopMap* map = nullptr; OopMapSet *oop_maps = new OopMapSet(); @@ -2886,6 +2883,9 @@ void SharedRuntime::generate_deopt_blob() { const Register exec_mode_reg = R21_tmp1; const address start = __ pc(); + int exception_offset = 0; + int exception_in_tls_offset = 0; + int reexecute_offset = 0; #if defined(COMPILER1) || defined(COMPILER2) // -------------------------------------------------------------------------- @@ -2925,7 +2925,7 @@ void SharedRuntime::generate_deopt_blob() { // - R3_ARG1: exception oop // - R4_ARG2: exception pc - int exception_offset = __ pc() - start; + exception_offset = __ pc() - start; BLOCK_COMMENT("Prolog for exception case"); @@ -2936,7 +2936,7 @@ void SharedRuntime::generate_deopt_blob() { __ std(R4_ARG2, _abi0(lr), R1_SP); // Vanilla deoptimization with an exception pending in exception_oop. - int exception_in_tls_offset = __ pc() - start; + exception_in_tls_offset = __ pc() - start; // Push the "unpack frame". // Save everything in sight. @@ -2949,8 +2949,6 @@ void SharedRuntime::generate_deopt_blob() { __ li(exec_mode_reg, Deoptimization::Unpack_exception); // fall through - - int reexecute_offset = 0; #ifdef COMPILER1 __ b(exec_mode_initialized); @@ -3068,11 +3066,12 @@ void SharedRuntime::generate_deopt_blob() { // Return to the interpreter entry point. __ blr(); - __ flush(); -#else // COMPILER2 +#else // !defined(COMPILER1) && !defined(COMPILER2) __ unimplemented("deopt blob needed only with compiler"); - int exception_offset = __ pc() - start; -#endif // COMPILER2 +#endif + + // Make sure all code is generated + __ flush(); _deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, reexecute_offset, first_frame_size_in_bytes / wordSize); From 77e680b11efea0eb707b72f4f3bb9e3422e170fd Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Fri, 6 Feb 2026 12:52:43 +0000 Subject: [PATCH 154/215] 8376269: Mixed jstack cannot find function in vDSO Reviewed-by: kevinw, cjplummer --- .../linux/native/libsaproc/libproc_impl.h | 5 +- .../linux/native/libsaproc/ps_core.c | 55 ++++++++++- .../sa/LingeredAppWithVDSOCall.java | 61 ++++++++++++ .../TestJhsdbJstackMixedWithVDSOCallCore.java | 96 +++++++++++++++++++ test/lib/jdk/test/lib/apps/LingeredApp.java | 16 +++- 5 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java create mode 100644 test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h index 42a6212510c..262e99f4a64 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, 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 @@ -95,6 +95,9 @@ struct core_data { // part of the class sharing workaround int classes_jsa_fd; // file descriptor of class share archive uintptr_t dynamic_addr; // address of dynamic section of a.out + uintptr_t vdso_addr; // address of vDSO + off64_t vdso_offset; // offset of vDSO in core + size_t vdso_size; // size of vDSO uintptr_t ld_base_addr; // base address of ld.so size_t num_maps; // number of maps. map_info* maps; // maps in a linked list diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c index 899d42152d1..61bd1e80005 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include "libproc_impl.h" #include "ps_core_common.h" #include "proc_service.h" @@ -285,6 +288,8 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { // We will adjust it in read_exec_segments(). ph->core->dynamic_addr = auxv->a_un.a_val; break; + } else if (auxv->a_type == AT_SYSINFO_EHDR) { + ph->core->vdso_addr = auxv->a_un.a_val; } auxv++; } @@ -350,6 +355,10 @@ static bool read_core_segments(struct ps_prochandle* ph, ELF_EHDR* core_ehdr) { print_error("failed to add map info\n"); goto err; } + if (core_php->p_vaddr == ph->core->vdso_addr) { + ph->core->vdso_offset = core_php->p_offset; + ph->core->vdso_size = core_php->p_memsz; + } } break; } @@ -593,6 +602,39 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f return load_addr; } +// Check for vDSO binary in kernel directory (/lib/modules//vdso), +// rewrite the given lib_name string if found. +// Otherwise copy vDSO memory in coredump to temporal memory generated by +// memfd_create(). +// Returns FD for vDSO (should be closed by caller), or -1 on error. +static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name_len) { + int lib_fd; + struct utsname uts; + uname(&uts); + + // Check vDSO binary first (for referring debuginfo if possible). + char *vdso_path = (char*)malloc(lib_name_len); + snprintf(vdso_path, lib_name_len, "/lib/modules/%s/vdso/vdso64.so", uts.release); + lib_fd = pathmap_open(vdso_path); + if (lib_fd != -1) { + print_debug("replace vDSO: %s -> %s\n", lib_name, vdso_path); + strncpy(lib_name, vdso_path, lib_name_len); + } else { + // Copy vDSO memory segment from core to temporal memory + // if vDSO binary is not available. + lib_fd = memfd_create("[vdso] in core", 0); + off64_t ofs = ph->core->vdso_offset; + if (sendfile64(lib_fd, ph->core->core_fd, &ofs, ph->core->vdso_size) == -1) { + print_debug("can't copy vDSO (%d)\n", errno); + close(lib_fd); + lib_fd = -1; + } + } + + free(vdso_path); + return lib_fd; +} + // read shared library info from runtime linker's data structures. // This work is done by librtlb_db in Solaris static bool read_shared_lib_info(struct ps_prochandle* ph) { @@ -687,9 +729,14 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { // it will fail later. } - if (lib_name[0] != '\0') { - // ignore empty lib names - lib_fd = pathmap_open(lib_name); + if (lib_name[0] != '\0') { // ignore empty lib names + // We can use lib_base_diff to compare with vdso_addr + // because base address of vDSO should be 0. + if (lib_base_diff == ph->core->vdso_addr) { + lib_fd = handle_vdso(ph, lib_name, sizeof(lib_name)); + } else { + lib_fd = pathmap_open(lib_name); + } if (lib_fd < 0) { print_debug("can't open shared object %s\n", lib_name); diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java new file mode 100644 index 00000000000..d893ddf6f89 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java @@ -0,0 +1,61 @@ + +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.invoke.MethodHandle; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.ValueLayout; + +import jdk.test.lib.Asserts; +import jdk.test.lib.apps.LingeredApp; + + +public class LingeredAppWithVDSOCall extends LingeredApp { + + private static final MethodHandle gettimeofday; + + static { + var desc = FunctionDescriptor.of(ValueLayout.JAVA_INT, // return + ValueLayout.JAVA_LONG, // tv + ValueLayout.JAVA_LONG); // tz + var linker = Linker.nativeLinker(); + var gettimeofdayPtr = linker.defaultLookup().findOrThrow("gettimeofday"); + gettimeofday = linker.downcallHandle(gettimeofdayPtr, desc); + } + + private static void crashAtGettimeofday(long tvAddr, long tzAddr) { + try { + gettimeofday.invoke(tvAddr, tzAddr); + } catch (Throwable t) { + throw new RuntimeException(t); + } + Asserts.fail("gettimeofday() didn't crash"); + } + + public static void main(String[] args) { + setCrasher(() -> crashAtGettimeofday(100L, 200L)); + LingeredApp.main(args); + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java new file mode 100644 index 00000000000..84da46e272f --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; + +import jtreg.SkippedException; + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.Platform; +import jdk.test.lib.SA.SATestUtils; +import jdk.test.lib.Utils; +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.util.CoreUtils; + +/** + * @test + * @bug 8376269 + * @requires (os.family == "linux") & (vm.hasSA) + * @requires os.arch == "amd64" + * @library /test/lib + * @run driver TestJhsdbJstackMixedWithVDSOCallCore + */ +public class TestJhsdbJstackMixedWithVDSOCallCore { + + private static void runJstackMixed(String coreFileName) throws Exception { + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); + launcher.addVMArgs(Utils.getTestJavaOpts()); + launcher.addToolArg("jstack"); + launcher.addToolArg("--mixed"); + launcher.addToolArg("--exe"); + launcher.addToolArg(JDKToolFinder.getTestJDKTool("java")); + launcher.addToolArg("--core"); + launcher.addToolArg(coreFileName); + + ProcessBuilder pb = SATestUtils.createProcessBuilder(launcher); + Process jhsdb = pb.start(); + OutputAnalyzer out = new OutputAnalyzer(jhsdb); + + jhsdb.waitFor(); + + System.out.println(out.getStdout()); + System.err.println(out.getStderr()); + + out.shouldContain("vdso_gettimeofday"); + } + + private static void checkVDSODebugInfo() { + var kernelVersion = System.getProperty("os.version"); + var vdso = Path.of("/lib", "modules", kernelVersion, "vdso", "vdso64.so"); + if (SATestUtils.getDebugInfo(vdso.toString()) == null) { + // Skip this test if debuginfo of vDSO not found because internal + // function of gettimeofday() would not be exported, and vDSO + // binary might be stripped. + throw new SkippedException("vDSO debuginfo not found (" + vdso.toString() + ")"); + } + } + + public static void main(String... args) throws Throwable { + if (Platform.isMusl()) { + throw new SkippedException("This test does not work on musl libc."); + } + checkVDSODebugInfo(); + + var app = new LingeredAppWithVDSOCall(); + app.setForceCrash(true); + LingeredApp.startApp(app, CoreUtils.getAlwaysPretouchArg(true)); + app.waitAppTerminate(); + + String crashOutput = app.getOutput().getStdout(); + String coreFileName = CoreUtils.getCoreFileLocation(crashOutput, app.getPid()); + runJstackMixed(coreFileName); + } +} diff --git a/test/lib/jdk/test/lib/apps/LingeredApp.java b/test/lib/jdk/test/lib/apps/LingeredApp.java index 13008e68c54..38ad9ae5b0e 100644 --- a/test/lib/jdk/test/lib/apps/LingeredApp.java +++ b/test/lib/jdk/test/lib/apps/LingeredApp.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 @@ -122,6 +122,12 @@ public class LingeredApp { this.forceCrash = forceCrash; } + private static Runnable crasher; + + public static void setCrasher(Runnable runnable) { + crasher = runnable; + } + native private static int crash(); /** @@ -628,8 +634,12 @@ public class LingeredApp { synchronized(steadyStateObj) { startSteadyStateThread(steadyStateObj); if (forceCrash) { - System.loadLibrary("LingeredApp"); // location of native crash() method - crash(); + if (crasher == null) { + System.loadLibrary("LingeredApp"); // location of native crash() method + crash(); + } else { + crasher.run(); + } } while (Files.exists(path)) { // Touch the lock to indicate our readiness From 8620e67c87cf561c858c2528b3b00b016eec3a51 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Fri, 6 Feb 2026 12:53:03 +0000 Subject: [PATCH 155/215] 8377231: Build jpackage with SIZE optimization Reviewed-by: erikj, asemenyuk --- make/modules/jdk.jpackage/Lib.gmk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/make/modules/jdk.jpackage/Lib.gmk b/make/modules/jdk.jpackage/Lib.gmk index 704436bbde6..86b11bdafee 100644 --- a/make/modules/jdk.jpackage/Lib.gmk +++ b/make/modules/jdk.jpackage/Lib.gmk @@ -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 @@ -54,7 +54,7 @@ $(eval $(call SetupJdkExecutable, BUILD_JPACKAGEAPPLAUNCHER, \ SRC := applauncher, \ EXTRA_SRC := common, \ INCLUDE_FILES := $(JPACKAGEAPPLAUNCHER_INCLUDE_FILES), \ - OPTIMIZATION := LOW, \ + OPTIMIZATION := SIZE, \ DISABLED_WARNINGS_clang_JvmLauncherLib.c := format-nonliteral, \ DISABLED_WARNINGS_clang_LinuxPackage.c := format-nonliteral, \ DISABLED_WARNINGS_clang_Log.cpp := unused-const-variable, \ @@ -91,7 +91,7 @@ ifeq ($(call isTargetOs, linux), true) common, \ EXCLUDE_FILES := LinuxLauncher.c LinuxPackage.c, \ LINK_TYPE := C++, \ - OPTIMIZATION := LOW, \ + OPTIMIZATION := SIZE, \ DISABLED_WARNINGS_gcc_Log.cpp := unused-const-variable, \ DISABLED_WARNINGS_clang_JvmLauncherLib.c := format-nonliteral, \ DISABLED_WARNINGS_clang_tstrings.cpp := format-nonliteral, \ From d1b226dec293804cd6f929c4a46ae59cb246253e Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Fri, 6 Feb 2026 13:40:54 +0000 Subject: [PATCH 156/215] 8376264: Mixed jstack could not unwind optimized frame Reviewed-by: cjplummer, kevinw --- .../linux/native/libsaproc/DwarfParser.cpp | 17 +- .../linux/native/libsaproc/dwarf.cpp | 24 ++- .../linux/native/libsaproc/dwarf.hpp | 13 +- .../debugger/linux/LinuxCDebugger.java | 8 +- .../debugger/linux/amd64/DwarfParser.java | 12 +- .../linux/amd64/LinuxAMD64CFrame.java | 187 ++++++++---------- .../sa/TestJhsdbJstackMixedCore.java | 10 +- 7 files changed, 122 insertions(+), 149 deletions(-) diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp index 8df08c49e09..6c94992e1e2 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/DwarfParser.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, 2021, NTT DATA. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, NTT DATA. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -219,16 +219,3 @@ JNIEXPORT jint JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_get DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); return parser->get_bp_cfa_offset(); } - -/* - * Class: sun_jvm_hotspot_debugger_linux_amd64_DwarfParser - * Method: isBPOffsetAvailable - * Signature: ()Z - */ -extern "C" -JNIEXPORT jboolean JNICALL Java_sun_jvm_hotspot_debugger_linux_amd64_DwarfParser_isBPOffsetAvailable - (JNIEnv *env, jobject this_obj) { - DwarfParser *parser = reinterpret_cast(get_dwarf_context(env, this_obj)); - return parser->is_bp_offset_available(); -} - diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp index a0c54230530..2636bdf691a 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, NTT DATA. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, NTT DATA. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -99,12 +99,11 @@ bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) { // Clear state _current_pc = 0L; - _cfa_reg = RSP; + _cfa_reg = MAX_VALUE; _return_address_reg = RA; _cfa_offset = 0; - _ra_cfa_offset = 0; - _bp_cfa_offset = 0; - _bp_offset_available = false; + _ra_cfa_offset = 8; + _bp_cfa_offset = INT_MAX; parse_dwarf_instructions(0L, static_cast(-1L), end); @@ -119,8 +118,8 @@ void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const /* for remember state */ enum DWARF_Register rem_cfa_reg = MAX_VALUE; int rem_cfa_offset = 0; - int rem_ra_cfa_offset = 0; - int rem_bp_cfa_offset = 0; + int rem_ra_cfa_offset = 8; + int rem_bp_cfa_offset = INT_MAX; while ((_buf < end) && (_current_pc < pc)) { unsigned char op = *_buf++; @@ -147,7 +146,6 @@ void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const enum DWARF_Register reg = static_cast(opa); if (reg == RBP) { _bp_cfa_offset = operand1 * _data_factor; - _bp_offset_available = true; } else if (reg == RA) { _ra_cfa_offset = operand1 * _data_factor; } @@ -184,6 +182,14 @@ void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const } break; } + case 0x07: { // DW_CFA_undefined + enum DWARF_Register reg = static_cast(read_leb(false)); + // We are only interested in BP here because CFA and RA should not be undefined. + if (reg == RBP) { + _bp_cfa_offset = INT_MAX; + } + break; + } case 0x0d: {// DW_CFA_def_cfa_register _cfa_reg = static_cast(read_leb(false)); break; diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp index a047ffae247..a2692738ce1 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, NTT DATA. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, NTT DATA. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,7 +73,6 @@ class DwarfParser { int _cfa_offset; int _ra_cfa_offset; int _bp_cfa_offset; - bool _bp_offset_available; uintptr_t read_leb(bool sign); uint64_t get_entry_length(); @@ -86,15 +85,14 @@ class DwarfParser { DwarfParser(lib_info *lib) : _lib(lib), _buf(NULL), _encoding(0), - _cfa_reg(RSP), + _cfa_reg(MAX_VALUE), _return_address_reg(RA), _code_factor(0), _data_factor(0), _current_pc(0L), _cfa_offset(0), - _ra_cfa_offset(0), - _bp_cfa_offset(0), - _bp_offset_available(false) {}; + _ra_cfa_offset(8), + _bp_cfa_offset(INT_MAX) {}; ~DwarfParser() {} bool process_dwarf(const uintptr_t pc); @@ -102,7 +100,6 @@ class DwarfParser { int get_cfa_offset() { return _cfa_offset; } int get_ra_cfa_offset() { return _ra_cfa_offset; } int get_bp_cfa_offset() { return _bp_cfa_offset; } - bool is_bp_offset_available() { return _bp_offset_available; } bool is_in(long pc) { return (_lib->exec_start <= pc) && (pc < _lib->exec_end); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java index e3543503216..15f6615421c 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxCDebugger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -81,11 +81,7 @@ class LinuxCDebugger implements CDebugger { String cpu = dbg.getCPU(); if (cpu.equals("amd64")) { AMD64ThreadContext context = (AMD64ThreadContext) thread.getContext(); - Address sp = context.getRegisterAsAddress(AMD64ThreadContext.RSP); - if (sp == null) return null; - Address pc = context.getRegisterAsAddress(AMD64ThreadContext.RIP); - if (pc == null) return null; - return LinuxAMD64CFrame.getTopFrame(dbg, sp, pc, context); + return LinuxAMD64CFrame.getTopFrame(dbg, context); } else if (cpu.equals("ppc64")) { PPC64ThreadContext context = (PPC64ThreadContext) thread.getContext(); Address sp = context.getRegisterAsAddress(PPC64ThreadContext.SP); diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java index 3b63ee0a21e..53351c918d3 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/DwarfParser.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, NTT DATA. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, NTT DATA. * 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,9 +66,15 @@ public class DwarfParser { processDwarf0(pc.asLongValue()); } + /** + * @return true if BP offset is declared in DWARF instructions. + */ + public boolean isBPOffsetAvailable() { + return getBasePointerOffsetFromCFA() != Integer.MAX_VALUE; + } + public native int getCFARegister(); public native int getCFAOffset(); public native int getReturnAddressOffsetFromCFA(); public native int getBasePointerOffsetFromCFA(); - public native boolean isBPOffsetAvailable(); } diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java index 0ec7f1949bd..4d3d9d5998d 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/amd64/LinuxAMD64CFrame.java @@ -40,8 +40,9 @@ public final class LinuxAMD64CFrame extends BasicCFrame { private static LinuxAMD64CFrame getFrameFromReg(LinuxDebugger dbg, Function getreg) { Address rip = getreg.apply(AMD64ThreadContext.RIP); Address rsp = getreg.apply(AMD64ThreadContext.RSP); + Address rbp = getreg.apply(AMD64ThreadContext.RBP); Address libptr = dbg.findLibPtrByAddress(rip); - Address cfa = getreg.apply(AMD64ThreadContext.RBP); + Address cfa = null; DwarfParser dwarf = null; if (libptr != null) { // Native frame @@ -52,61 +53,34 @@ public final class LinuxAMD64CFrame extends BasicCFrame { // DWARF processing should succeed when the frame is native // but it might fail if Common Information Entry (CIE) has language // personality routine and/or Language Specific Data Area (LSDA). - return new LinuxAMD64CFrame(dbg, rsp, cfa, rip, dwarf, true); + return new LinuxAMD64CFrame(dbg, rsp, rbp, cfa, rip, dwarf, true); } cfa = getreg.apply(dwarf.getCFARegister()) .addOffsetTo(dwarf.getCFAOffset()); } - return (cfa == null) ? null - : new LinuxAMD64CFrame(dbg, rsp, cfa, rip, dwarf); + return (rbp == null && cfa == null) + ? null + : new LinuxAMD64CFrame(dbg, rsp, rbp, cfa, rip, dwarf); } - public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, Address rip, ThreadContext context) { + public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, ThreadContext context) { return getFrameFromReg(dbg, context::getRegisterAsAddress); } - public static LinuxAMD64CFrame getTopFrame(LinuxDebugger dbg, Address rsp, Address rip, ThreadContext context) { - Address libptr = dbg.findLibPtrByAddress(rip); - Address cfa = context.getRegisterAsAddress(AMD64ThreadContext.RBP); - DwarfParser dwarf = null; - - if (libptr != null) { // Native frame - dwarf = new DwarfParser(libptr); - try { - dwarf.processDwarf(rip); - } catch (DebuggerException e) { - // DWARF processing should succeed when the frame is native - // but it might fail if Common Information Entry (CIE) has language - // personality routine and/or Language Specific Data Area (LSDA). - return new LinuxAMD64CFrame(dbg, rsp, cfa, rip, dwarf, true); - } - - cfa = context.getRegisterAsAddress(dwarf.getCFARegister()) - .addOffsetTo(dwarf.getCFAOffset()); - } - - return (cfa == null) ? null - : new LinuxAMD64CFrame(dbg, rsp, cfa, rip, dwarf); + private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf) { + this(dbg, rsp, rbp, cfa, rip, dwarf, false); } - private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address cfa, Address rip, DwarfParser dwarf) { - this(dbg, rsp, cfa, rip, dwarf, false); - } - - private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address cfa, Address rip, DwarfParser dwarf, boolean finalFrame) { - this(dbg, rsp, cfa, rip, dwarf, finalFrame, false); - } - - private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address cfa, Address rip, DwarfParser dwarf, boolean finalFrame, boolean use1ByteBeforeToLookup) { + private LinuxAMD64CFrame(LinuxDebugger dbg, Address rsp, Address rbp, Address cfa, Address rip, DwarfParser dwarf, boolean use1ByteBeforeToLookup) { super(dbg.getCDebugger()); this.rsp = rsp; + this.rbp = rbp; this.cfa = cfa; this.rip = rip; this.dbg = dbg; this.dwarf = dwarf; - this.finalFrame = finalFrame; this.use1ByteBeforeToLookup = use1ByteBeforeToLookup; } @@ -127,82 +101,74 @@ public final class LinuxAMD64CFrame extends BasicCFrame { } public Address localVariableBase() { - return cfa; + return (dwarf != null && dwarf.isBPOffsetAvailable()) + ? cfa.addOffsetTo(dwarf.getBasePointerOffsetFromCFA()) + : rbp; } - private Address getNextPC(boolean useDwarf) { + private Address getNextPC() { try { - long offs = useDwarf ? dwarf.getReturnAddressOffsetFromCFA() - : ADDRESS_SIZE; - return cfa.getAddressAt(offs); + return dwarf == null + ? rbp.getAddressAt(ADDRESS_SIZE) // Java frame + : cfa.getAddressAt(dwarf.getReturnAddressOffsetFromCFA()); // Native frame } catch (UnmappedAddressException | UnalignedAddressException e) { return null; } } - private boolean isValidFrame(Address nextCFA, boolean isNative) { - // CFA should never be null. - // nextCFA must be greater than current CFA, if frame is native. - // Java interpreter frames can share the CFA (frame pointer). - return nextCFA != null && - (!isNative || (isNative && nextCFA.greaterThan(cfa))); + private boolean isValidFrame(Address nextCFA, Address nextRBP) { + // Both CFA and RBP must not be null. + if (nextCFA == null && nextRBP == null) { + return false; + } + + // RBP must not be null if CFA is null - it happens between Java frame and Native frame. + // We cannot validate RBP value because it might be used as GPR. Thus returns true + // if RBP is not null. + if (nextCFA == null && nextRBP != null) { + return true; + } + + // nextCFA must be greater than current CFA. + if (nextCFA != null && nextCFA.greaterThanOrEqual(cfa)) { + return true; + } + + // Otherwise, the frame is not valid. + return false; } private Address getNextRSP() { - // next RSP should be previous slot of return address. - var bp = dwarf == null ? cfa.addOffsetTo(ADDRESS_SIZE) // top of BP points callser BP - : cfa.addOffsetTo(dwarf.getReturnAddressOffsetFromCFA()); - return bp.addOffsetTo(ADDRESS_SIZE); + return dwarf == null ? rbp.addOffsetTo(2 * ADDRESS_SIZE) // Java frame - skip saved BP and RA + : cfa.addOffsetTo(dwarf.getReturnAddressOffsetFromCFA()) + .addOffsetTo(ADDRESS_SIZE); // Native frame } - private Address getNextCFA(DwarfParser nextDwarf, ThreadContext context, Address senderFP, Address senderPC) { - Address nextCFA; - boolean isNative = false; - - if (senderFP == null) { - senderFP = cfa.getAddressAt(0); // RBP by default + private Address getNextRBP(Address senderFP) { + if (senderFP != null) { + return senderFP; + } else if (dwarf == null) { // Current frame is Java + return rbp.getAddressAt(0); + } else { // Current frame is Native + return dwarf.isBPOffsetAvailable() + ? cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()) + : rbp; } + } - if (VM.getVM().getCodeCache().contains(senderPC)) { // Next frame is Java - nextCFA = (dwarf == null) ? senderFP // Current frame is Java - : cfa.getAddressAt(dwarf.getBasePointerOffsetFromCFA()); // Current frame is Native - } else { // Next frame is Native - if (VM.getVM().getCodeCache().contains(pc())) { // Current frame is Java - nextCFA = senderFP.addOffsetTo(-nextDwarf.getBasePointerOffsetFromCFA()); - } else { // Current frame is Native - if (nextDwarf == null) { // maybe runtime entrypoint (_start()) - throw new DebuggerException("nextDwarf is null even though native call"); - } - - isNative = true; - int nextCFAReg = nextDwarf.getCFARegister(); - if (nextCFAReg == AMD64ThreadContext.RBP) { - Address rbp = dwarf.isBPOffsetAvailable() ? cfa.addOffsetTo(dwarf.getBasePointerOffsetFromCFA()) - : context.getRegisterAsAddress(AMD64ThreadContext.RBP); - Address nextRBP = rbp.getAddressAt(0); - nextCFA = nextRBP.addOffsetTo(-nextDwarf.getBasePointerOffsetFromCFA()); - } else if (nextCFAReg == AMD64ThreadContext.RSP) { - nextCFA = getNextRSP().addOffsetTo(nextDwarf.getCFAOffset()); - } else { - throw new DebuggerException("Unsupported CFA register: " + nextCFAReg); - } - } - } - - // Sanity check for next CFA address - try { - nextCFA.getAddressAt(0); - } catch (Exception e) { - // return null if next CFA address is invalid + private Address getNextCFA(DwarfParser nextDwarf, Address senderFP, Address senderPC) { + if (nextDwarf == null) { // Next frame is Java + // CFA is not available on Java frame return null; } - if (dbg.isSignalTrampoline(senderPC)) { - // Return without frame check if sender is signal trampoline. - return nextCFA; - } else { - return isValidFrame(nextCFA, isNative) ? nextCFA : null; - } + // Next frame is Native + int nextCFAReg = nextDwarf.getCFARegister(); + return switch(nextCFAReg){ + case AMD64ThreadContext.RBP -> getNextRBP(senderFP).addOffsetTo(nextDwarf.getCFAOffset()); + case AMD64ThreadContext.RSP -> getNextRSP().addOffsetTo(nextDwarf.getCFAOffset()); + default -> throw new DebuggerException("Unsupported CFA register: " + nextCFAReg); + }; } @Override @@ -212,10 +178,6 @@ public final class LinuxAMD64CFrame extends BasicCFrame { @Override public CFrame sender(ThreadProxy th, Address sp, Address fp, Address pc) { - if (finalFrame) { - return null; - } - if (dbg.isSignalTrampoline(pc())) { // RSP points signal context // https://github.com/torvalds/linux/blob/v6.17/arch/x86/kernel/signal.c#L94 @@ -228,8 +190,7 @@ public final class LinuxAMD64CFrame extends BasicCFrame { if (nextRSP == null) { return null; } - - Address nextPC = pc != null ? pc : getNextPC(dwarf != null); + Address nextPC = pc != null ? pc : getNextPC(); if (nextPC == null) { return null; } @@ -252,11 +213,23 @@ public final class LinuxAMD64CFrame extends BasicCFrame { } } + Address nextRBP = getNextRBP(fp); + try { - Address nextCFA = getNextCFA(nextDwarf, context, fp, nextPC); - return new LinuxAMD64CFrame(dbg, nextRSP, nextCFA, nextPC, nextDwarf, false, fallback); - } catch (DebuggerException _) { - return null; + Address nextCFA = getNextCFA(nextDwarf, fp, nextPC); + return isValidFrame(nextCFA, nextRBP) + ? new LinuxAMD64CFrame(dbg, nextRSP, nextRBP, nextCFA, nextPC, nextDwarf, fallback) + : null; + } catch (DebuggerException e) { + if (dbg.isSignalTrampoline(nextPC)) { + // We can through the caller frame if it is signal trampoline. + // getNextCFA() might fail because DwarfParser cannot find out CFA register. + return new LinuxAMD64CFrame(dbg, nextRSP, nextRBP, null, nextPC, nextDwarf, fallback); + } + + // Rethrow the original exception if getNextCFA() failed + // and the caller is not signal trampoline. + throw e; } } @@ -280,16 +253,16 @@ public final class LinuxAMD64CFrame extends BasicCFrame { @Override public Frame toFrame() { - return new AMD64Frame(rsp, cfa, rip); + return new AMD64Frame(rsp, localVariableBase(), rip); } // package/class internals only private static final int ADDRESS_SIZE = 8; private Address rsp; + private Address rbp; private Address rip; private Address cfa; private LinuxDebugger dbg; private DwarfParser dwarf; - private boolean finalFrame; private boolean use1ByteBeforeToLookup; } diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java index 9fc304d1854..d781e8c5c09 100644 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedCore.java @@ -22,8 +22,11 @@ * questions. */ +import jtreg.SkippedException; + import jdk.test.lib.JDKToolFinder; import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.Platform; import jdk.test.lib.SA.SATestUtils; import jdk.test.lib.Utils; import jdk.test.lib.apps.LingeredApp; @@ -34,7 +37,7 @@ import jtreg.SkippedException; /** * @test - * @bug 8374482 8376284 + * @bug 8374482 8376264 8376284 * @requires (os.family == "linux") & (vm.hasSA) * @requires os.arch == "amd64" * @library /test/lib @@ -63,9 +66,14 @@ public class TestJhsdbJstackMixedCore { out.shouldContain("__restore_rt "); out.shouldContain("Java_jdk_test_lib_apps_LingeredApp_crash"); + out.shouldContain("* jdk.test.lib.apps.LingeredApp.crash()"); } public static void main(String... args) throws Throwable { + if (Platform.isMusl()) { + throw new SkippedException("This test does not work on musl libc."); + } + // Check whether the symbol of signal trampoline is available. var libc = SATestUtils.getLibCPath(); From 77e8469fb0a67d4a795f049acee7e67eaedfb5b7 Mon Sep 17 00:00:00 2001 From: Andrew Haley Date: Fri, 6 Feb 2026 13:50:54 +0000 Subject: [PATCH 157/215] 8328306: AArch64: MacOS lazy JIT "write xor execute" switching Co-authored-by: Dean Long Reviewed-by: dlong, adinn --- src/hotspot/cpu/aarch64/assembler_aarch64.hpp | 1 + .../gc/shared/barrierSetNMethod_aarch64.cpp | 4 + .../cpu/aarch64/macroAssembler_aarch64.cpp | 5 + .../cpu/aarch64/nativeInst_aarch64.cpp | 4 +- .../cpu/aarch64/nativeInst_aarch64.hpp | 21 +++-- .../cpu/aarch64/stubGenerator_aarch64.cpp | 4 +- .../cpu/aarch64/vm_version_aarch64.cpp | 16 ++++ src/hotspot/os/bsd/globals_bsd.hpp | 19 +++- src/hotspot/os/bsd/os_bsd.cpp | 1 + .../os_cpu/bsd_aarch64/os_bsd_aarch64.cpp | 91 +++++++++++++++++-- src/hotspot/share/asm/codeBuffer.cpp | 2 + src/hotspot/share/c1/c1_Runtime1.cpp | 1 + src/hotspot/share/classfile/classLoader.cpp | 2 + src/hotspot/share/classfile/classLoader.hpp | 4 + src/hotspot/share/code/codeBlob.cpp | 2 + src/hotspot/share/code/nmethod.cpp | 8 ++ src/hotspot/share/code/vtableStubs.cpp | 3 + .../share/gc/shared/barrierSetNMethod.cpp | 2 + src/hotspot/share/memory/heap.cpp | 6 ++ src/hotspot/share/opto/runtime.cpp | 2 + src/hotspot/share/prims/upcallStubs.cpp | 1 + src/hotspot/share/runtime/deoptimization.cpp | 1 + .../share/runtime/interfaceSupport.inline.hpp | 31 +++++-- src/hotspot/share/runtime/javaCalls.cpp | 29 +++--- src/hotspot/share/runtime/javaThread.cpp | 14 +-- src/hotspot/share/runtime/javaThread.hpp | 10 ++ src/hotspot/share/runtime/os.hpp | 25 ++++- src/hotspot/share/runtime/thread.hpp | 9 +- src/hotspot/share/runtime/thread.inline.hpp | 20 +++- .../share/runtime/threadWXSetters.inline.hpp | 55 +++++++++-- .../share/utilities/forbiddenFunctions.hpp | 4 + src/hotspot/share/utilities/macros.hpp | 3 + .../utilities/permitForbiddenFunctions.hpp | 4 + .../jtreg/runtime/os/TestWXHealing.java | 58 ++++++++++++ test/hotspot/jtreg/runtime/os/WXHealing.java | 48 ++++++++++ 35 files changed, 436 insertions(+), 74 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/os/TestWXHealing.java create mode 100644 test/hotspot/jtreg/runtime/os/WXHealing.java diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index fc6e58b801c..19b3bb1a65b 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -4329,6 +4329,7 @@ public: #undef INSN Assembler(CodeBuffer* code) : AbstractAssembler(code) { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); } // Stack overflow checking diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp index 4d5ca01b6b4..3d5261c31d1 100644 --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp @@ -209,6 +209,10 @@ void BarrierSetNMethod::set_guard_value(nmethod* nm, int value, int bit_mask) { bs_asm->increment_patching_epoch(); } + // Enable WXWrite: the function is called directly from nmethod_entry_barrier + // stub. + MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current())); + NativeNMethodBarrier barrier(nm); barrier.set_value(value, bit_mask); } diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index ba42602ddc1..409343b6b8d 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -473,6 +473,7 @@ address MacroAssembler::target_addr_for_insn(address insn_addr) { // Patch any kind of instruction; there may be several instructions. // Return the total length (in bytes) of the instructions. int MacroAssembler::pd_patch_instruction_size(address insn_addr, address target) { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); return RelocActions::run(insn_addr, target); } @@ -481,6 +482,8 @@ int MacroAssembler::patch_oop(address insn_addr, address o) { unsigned insn = *(unsigned*)insn_addr; assert(nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch"); + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); + // OOPs are either narrow (32 bits) or wide (48 bits). We encode // narrow OOPs by setting the upper 16 bits in the first // instruction. @@ -510,6 +513,8 @@ int MacroAssembler::patch_narrow_klass(address insn_addr, narrowKlass n) { assert(Instruction_aarch64::extract(insn->encoding(), 31, 21) == 0b11010010101 && nativeInstruction_at(insn_addr+4)->is_movk(), "wrong insns in patch"); + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); + Instruction_aarch64::patch(insn_addr, 20, 5, n >> 16); Instruction_aarch64::patch(insn_addr+4, 20, 5, n & 0xffff); return 2 * NativeInstruction::instruction_size; diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp index f2003dd9b55..0cdf36f0bc5 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp @@ -133,7 +133,6 @@ void NativeMovConstReg::verify() { intptr_t NativeMovConstReg::data() const { - // das(uint64_t(instruction_address()),2); address addr = MacroAssembler::target_addr_for_insn(instruction_address()); if (maybe_cpool_ref(instruction_address())) { return *(intptr_t*)addr; @@ -144,6 +143,7 @@ intptr_t NativeMovConstReg::data() const { void NativeMovConstReg::set_data(intptr_t x) { if (maybe_cpool_ref(instruction_address())) { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); address addr = MacroAssembler::target_addr_for_insn(instruction_address()); *(intptr_t*)addr = x; } else { @@ -350,8 +350,6 @@ bool NativeInstruction::is_stop() { //------------------------------------------------------------------- -void NativeGeneralJump::verify() { } - // MT-safe patching of a long jump instruction. void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) { ShouldNotCallThis(); diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp index c30cb911d96..15b6c9ff215 100644 --- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.hpp @@ -90,16 +90,18 @@ protected: s_char sbyte_at(int offset) const { return *(s_char*)addr_at(offset); } u_char ubyte_at(int offset) const { return *(u_char*)addr_at(offset); } - jint int_at(int offset) const { return *(jint*)addr_at(offset); } - juint uint_at(int offset) const { return *(juint*)addr_at(offset); } - address ptr_at(int offset) const { return *(address*)addr_at(offset); } - oop oop_at(int offset) const { return *(oop*)addr_at(offset); } + jint int_at(int offset) const { return *(jint*)addr_at(offset); } + juint uint_at(int offset) const { return *(juint*)addr_at(offset); } + address ptr_at(int offset) const { return *(address*)addr_at(offset); } + oop oop_at(int offset) const { return *(oop*)addr_at(offset); } - void set_char_at(int offset, char c) { *addr_at(offset) = (u_char)c; } - void set_int_at(int offset, jint i) { *(jint*)addr_at(offset) = i; } - void set_uint_at(int offset, jint i) { *(juint*)addr_at(offset) = i; } - void set_ptr_at(int offset, address ptr) { *(address*)addr_at(offset) = ptr; } - void set_oop_at(int offset, oop o) { *(oop*)addr_at(offset) = o; } +#define MACOS_WX_WRITE MACOS_AARCH64_ONLY(os::thread_wx_enable_write()) + void set_char_at(int offset, char c) { MACOS_WX_WRITE; *addr_at(offset) = (u_char)c; } + void set_int_at(int offset, jint i) { MACOS_WX_WRITE; *(jint*)addr_at(offset) = i; } + void set_uint_at(int offset, jint i) { MACOS_WX_WRITE; *(juint*)addr_at(offset) = i; } + void set_ptr_at(int offset, address ptr) { MACOS_WX_WRITE; *(address*)addr_at(offset) = ptr; } + void set_oop_at(int offset, oop o) { MACOS_WX_WRITE; *(oop*)addr_at(offset) = o; } +#undef MACOS_WX_WRITE void wrote(int offset); @@ -380,7 +382,6 @@ public: void set_jump_destination(address dest); static void replace_mt_safe(address instr_addr, address code_buffer); - static void verify(); }; inline NativeGeneralJump* nativeGeneralJump_at(address address) { diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index db653bcf236..a459a28b09e 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -11742,7 +11742,9 @@ class StubGenerator: public StubCodeGenerator { } #endif - StubRoutines::_unsafe_setmemory = generate_unsafe_setmemory(); + if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_setMemory)) { + StubRoutines::_unsafe_setmemory = generate_unsafe_setmemory(); + } StubRoutines::aarch64::set_completed(); // Inidicate that arraycopy and zero_blocks stubs are generated } diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 3fa85f8f47d..0a7bc5a8962 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -622,6 +622,22 @@ void VM_Version::initialize() { check_virtualizations(); +#ifdef __APPLE__ + DefaultWXWriteMode = UseOldWX ? WXWrite : WXArmedForWrite; + + if (TraceWXHealing) { + if (pthread_jit_write_protect_supported_np()) { + tty->print_cr("### TraceWXHealing is in use"); + if (StressWXHealing) { + tty->print_cr("### StressWXHealing is in use"); + } + } else { + tty->print_cr("WX Healing is not in use because MAP_JIT write protection " + "does not work on this system."); + } + } +#endif + // Sync SVE related CPU features with flags if (UseSVE < 2) { clear_feature(CPU_SVE2); diff --git a/src/hotspot/os/bsd/globals_bsd.hpp b/src/hotspot/os/bsd/globals_bsd.hpp index 850d491a11f..22f587ed789 100644 --- a/src/hotspot/os/bsd/globals_bsd.hpp +++ b/src/hotspot/os/bsd/globals_bsd.hpp @@ -28,6 +28,7 @@ // // Declare Bsd specific flags. They are not available on other platforms. // +#ifdef AARCH64 #define RUNTIME_OS_FLAGS(develop, \ develop_pd, \ product, \ @@ -35,9 +36,21 @@ range, \ constraint) \ \ - AARCH64_ONLY(develop(bool, AssertWXAtThreadSync, true, \ - "Conservatively check W^X thread state at possible safepoint" \ - "or handshake")) + develop(bool, TraceWXHealing, false, \ + "track occurrences of W^X mode healing") \ + develop(bool, UseOldWX, false, \ + "Choose old W^X implementation.") \ + product(bool, StressWXHealing, false, DIAGNOSTIC, \ + "Stress W xor X healing on MacOS") + +#else +#define RUNTIME_OS_FLAGS(develop, \ + develop_pd, \ + product, \ + product_pd, \ + range, \ + constraint) +#endif // end of RUNTIME_OS_FLAGS diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 0e21c2d1785..81320b4f1aa 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -841,6 +841,7 @@ jlong os::javaTimeNanos() { // We might also condition (c) on the magnitude of the delta between obsv and now. // Avoiding excessive CAS operations to hot RW locations is critical. // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate + // https://web.archive.org/web/20131214182431/https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate return (prev == obsv) ? now : obsv; } diff --git a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp index 62dba218b2f..36599594842 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp +++ b/src/hotspot/os_cpu/bsd_aarch64/os_bsd_aarch64.cpp @@ -54,8 +54,11 @@ #include "signals_posix.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" +#include "utilities/decoder.hpp" #include "utilities/events.hpp" +#include "utilities/nativeStackPrinter.hpp" #include "utilities/vmError.hpp" +#include "compiler/disassembler.hpp" // put OS-includes here # include @@ -85,6 +88,8 @@ #define SPELL_REG_SP "sp" #ifdef __APPLE__ +WXMode DefaultWXWriteMode; + // see darwin-xnu/osfmk/mach/arm/_structs.h // 10.5 UNIX03 member name prefixes @@ -233,19 +238,56 @@ NOINLINE frame os::current_frame() { bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, ucontext_t* uc, JavaThread* thread) { - // Enable WXWrite: this function is called by the signal handler at arbitrary - // point of execution. - ThreadWXEnable wx(WXWrite, thread); - // decide if this trap can be handled by a stub address stub = nullptr; - - address pc = nullptr; + address pc = nullptr; //%note os_trap_1 if (info != nullptr && uc != nullptr && thread != nullptr) { pc = (address) os::Posix::ucontext_get_pc(uc); +#ifdef MACOS_AARCH64 + // If we got a SIGBUS because we tried to write into the code + // cache, try enabling WXWrite mode. + if (sig == SIGBUS + && pc != info->si_addr + && CodeCache::contains(info->si_addr) + && os::address_is_in_vm(pc)) { + WXMode *entry_mode = thread->_cur_wx_mode; + if (entry_mode != nullptr && *entry_mode == WXArmedForWrite) { + if (TraceWXHealing) { + static const char *mode_names[3] = {"WXWrite", "WXExec", "WXArmedForWrite"}; + tty->print("Healing WXMode %s at %p to WXWrite", + mode_names[*entry_mode], entry_mode); + char name[128]; + int offset = 0; + if (os::dll_address_to_function_name(pc, name, sizeof name, &offset)) { + tty->print_cr(" (%s+0x%x)", name, offset); + } else { + tty->cr(); + } + if (Verbose) { + char buf[O_BUFLEN]; + NativeStackPrinter nsp(thread); + nsp.print_stack(tty, buf, sizeof(buf), pc, + true /* print_source_info */, -1 /* max stack */); + } + } +#ifndef PRODUCT + guarantee(StressWXHealing, + "We should not reach here unless StressWXHealing"); +#endif + *(thread->_cur_wx_mode) = WXWrite; + return thread->wx_enable_write(); + } + } + + // There may be cases where code after this point that we call + // from the signal handler changes WX state, so we protect against + // that by saving and restoring the state. + ThreadWXEnable wx(thread->get_wx_state(), thread); +#endif + // Handle ALL stack overflow variations here if (sig == SIGSEGV || sig == SIGBUS) { address addr = (address) info->si_addr; @@ -515,11 +557,42 @@ int os::extra_bang_size_in_bytes() { return 0; } -#ifdef __APPLE__ +#ifdef MACOS_AARCH64 +THREAD_LOCAL bool os::_jit_exec_enabled; + +// This is a wrapper around the standard library function +// pthread_jit_write_protect_np(3). We keep track of the state of +// per-thread write protection on the MAP_JIT region in the +// thread-local variable os::_jit_exec_enabled void os::current_thread_enable_wx(WXMode mode) { - pthread_jit_write_protect_np(mode == WXExec); + bool exec_enabled = mode != WXWrite; + if (exec_enabled != _jit_exec_enabled NOT_PRODUCT( || DefaultWXWriteMode == WXWrite)) { + permit_forbidden_function::pthread_jit_write_protect_np(exec_enabled); + _jit_exec_enabled = exec_enabled; + } } -#endif + +// If the current thread is in the WX state WXArmedForWrite, change +// the state to WXWrite. +bool Thread::wx_enable_write() { + if (_wx_state == WXArmedForWrite) { + _wx_state = WXWrite; + os::current_thread_enable_wx(WXWrite); + return true; + } else { + return false; + } +} + +// A wrapper around wx_enable_write() for when the current thread is +// not known. +void os::thread_wx_enable_write_impl() { + if (!StressWXHealing) { + Thread::current()->wx_enable_write(); + } +} + +#endif // MACOS_AARCH64 static inline void atomic_copy64(const volatile void *src, volatile void *dst) { *(jlong *) dst = *(const jlong *) src; diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index 7871134e923..d94f52c18f6 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -98,6 +98,8 @@ CodeBuffer::CodeBuffer(const CodeBlob* blob) DEBUG_ONLY(: Scrubber(this, sizeof( } void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); + // Always allow for empty slop around each section. int slop = (int) CodeSection::end_slop(); diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index a4c956ff5be..63764dd113a 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -541,6 +541,7 @@ extern void vm_exit(int code); // unpack_with_exception entry instead. This makes life for the exception blob easier // because making that same check and diverting is painful from assembly language. JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* current, oopDesc* ex, address pc, nmethod*& nm)) + MACOS_AARCH64_ONLY(current->wx_enable_write()); Handle exception(current, ex); // This function is called when we are about to throw an exception. Therefore, diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index f631bfaa102..eced83577cb 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -127,6 +127,7 @@ PerfCounter* ClassLoader::_perf_ik_link_methods_count = nullptr; PerfCounter* ClassLoader::_perf_method_adapters_count = nullptr; PerfCounter* ClassLoader::_unsafe_defineClassCallCounter = nullptr; PerfCounter* ClassLoader::_perf_secondary_hash_time = nullptr; +PerfCounter* ClassLoader::_perf_change_wx_time = nullptr; PerfCounter* ClassLoader::_perf_resolve_indy_time = nullptr; PerfCounter* ClassLoader::_perf_resolve_invokehandle_time = nullptr; @@ -1370,6 +1371,7 @@ void ClassLoader::initialize(TRAPS) { NEWPERFBYTECOUNTER(_perf_sys_classfile_bytes_read, SUN_CLS, "sysClassBytes"); NEWPERFEVENTCOUNTER(_unsafe_defineClassCallCounter, SUN_CLS, "unsafeDefineClassCalls"); NEWPERFTICKCOUNTER(_perf_secondary_hash_time, SUN_CLS, "secondarySuperHashTime"); + NEWPERFTICKCOUNTER(_perf_change_wx_time, SUN_CLS, "changeWXTime"); if (log_is_enabled(Info, perf, class, link)) { NEWPERFTICKCOUNTER(_perf_ik_link_methods_time, SUN_CLS, "linkMethodsTime"); diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp index afb0a581dcc..a935d3027ac 100644 --- a/src/hotspot/share/classfile/classLoader.hpp +++ b/src/hotspot/share/classfile/classLoader.hpp @@ -184,6 +184,7 @@ class ClassLoader: AllStatic { // Count the time taken to hash the scondary superclass arrays. static PerfCounter* _perf_secondary_hash_time; + static PerfCounter* _perf_change_wx_time; // The boot class path consists of 3 ordered pieces: // 1. the module/path pairs specified to --patch-module @@ -268,6 +269,9 @@ class ClassLoader: AllStatic { static PerfCounter* perf_secondary_hash_time() { return _perf_secondary_hash_time; } + static PerfCounter* perf_change_wx_time() { + return _perf_change_wx_time; + } static PerfCounter* perf_sys_classload_time() { return _perf_sys_classload_time; } static PerfCounter* perf_app_classload_time() { return _perf_app_classload_time; } static PerfCounter* perf_app_classload_selftime() { return _perf_app_classload_selftime; } diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index a0a34ec23fa..094b4f82cf0 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -520,6 +520,8 @@ VtableBlob* VtableBlob::create(const char* name, int buffer_size) { // eventually. return nullptr; } + + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); blob = new (size) VtableBlob(name, size); CodeCache_lock->unlock(); } diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index edfca5c98ee..13eb1ff1604 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -2137,6 +2137,9 @@ void nmethod::make_deoptimized() { ResourceMark rm; RelocIterator iter(this, oops_reloc_begin()); + // Assume there will be some calls to make deoptimized. + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); + while (iter.next()) { switch (iter.type()) { @@ -2213,6 +2216,7 @@ void nmethod::verify_clean_inline_caches() { } void nmethod::mark_as_maybe_on_stack() { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); AtomicAccess::store(&_gc_epoch, CodeCache::gc_epoch()); } @@ -2305,6 +2309,8 @@ bool nmethod::make_not_entrant(InvalidationReason invalidation_reason) { return false; } + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); + { // Enter critical section. Does not block for safepoint. ConditionalMutexLocker ml(NMethodState_lock, !NMethodState_lock->owned_by_self(), Mutex::_no_safepoint_check_flag); @@ -2740,6 +2746,8 @@ bool nmethod::is_unloading() { state_is_unloading = IsUnloadingBehaviour::is_unloading(this); uint8_t new_state = IsUnloadingState::create(state_is_unloading, state_unloading_cycle); + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); + // Note that if an nmethod has dead oops, everyone will agree that the // nmethod is_unloading. However, the is_cold heuristics can yield // different outcomes, so we guard the computed result with a CAS diff --git a/src/hotspot/share/code/vtableStubs.cpp b/src/hotspot/share/code/vtableStubs.cpp index b926888595d..35b226a8798 100644 --- a/src/hotspot/share/code/vtableStubs.cpp +++ b/src/hotspot/share/code/vtableStubs.cpp @@ -51,6 +51,9 @@ VMReg VtableStub::_receiver_location = VMRegImpl::Bad(); void* VtableStub::operator new(size_t size, int code_size) throw() { assert_lock_strong(VtableStubs_lock); assert(size == sizeof(VtableStub), "mismatched size"); + + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); + // compute real VtableStub size (rounded to nearest word) const int real_size = align_up(code_size + (int)sizeof(VtableStub), wordSize); // malloc them in chunks to minimize header overhead diff --git a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp index ab94bae079a..a1f03a4bf50 100644 --- a/src/hotspot/share/gc/shared/barrierSetNMethod.cpp +++ b/src/hotspot/share/gc/shared/barrierSetNMethod.cpp @@ -111,6 +111,8 @@ bool BarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) { return true; } + // Enable WXWrite: the function is called directly from nmethod_entry_barrier + // stub. MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current())); // If the nmethod is the only thing pointing to the oops, and we are using a diff --git a/src/hotspot/share/memory/heap.cpp b/src/hotspot/share/memory/heap.cpp index a1333ed13e9..3ade5169eef 100644 --- a/src/hotspot/share/memory/heap.cpp +++ b/src/hotspot/share/memory/heap.cpp @@ -163,6 +163,7 @@ void CodeHeap::mark_segmap_as_used(size_t beg, size_t end, bool is_FreeBlock_joi void CodeHeap::invalidate(size_t beg, size_t end, size_t hdr_size) { #ifndef PRODUCT + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); // Fill the given range with some bad value. // length is expected to be in segment_size units. // This prevents inadvertent execution of code leftover from previous use. @@ -172,11 +173,13 @@ void CodeHeap::invalidate(size_t beg, size_t end, size_t hdr_size) { } void CodeHeap::clear(size_t beg, size_t end) { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); mark_segmap_as_free(beg, end); invalidate(beg, end, 0); } void CodeHeap::clear() { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); _next_segment = 0; clear(_next_segment, _number_of_committed_segments); } @@ -190,6 +193,7 @@ static size_t align_to_page_size(size_t size) { bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_size) { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); assert(rs.size() >= committed_size, "reserved < committed"); assert(is_aligned(committed_size, rs.page_size()), "must be page aligned"); assert(segment_size >= sizeof(FreeBlock), "segment size is too small"); @@ -230,6 +234,7 @@ bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_s bool CodeHeap::expand_by(size_t size) { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); assert_locked_or_safepoint(CodeCache_lock); // expand _memory space @@ -259,6 +264,7 @@ bool CodeHeap::expand_by(size_t size) { void* CodeHeap::allocate(size_t instance_size) { + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); size_t number_of_segments = size_to_segments(instance_size + header_size()); assert(segments_to_size(number_of_segments) >= sizeof(FreeBlock), "not enough room for FreeList"); assert_locked_or_safepoint(CodeCache_lock); diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 6dbbfb0a130..0d2dbb813bd 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -1865,6 +1865,8 @@ JRT_ENTRY_NO_ASYNC(address, OptoRuntime::handle_exception_C_helper(JavaThread* c // has updated oops. StackWatermarkSet::after_unwind(current); + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); + // Do not confuse exception_oop with pending_exception. The exception_oop // is only used to pass arguments into the method. Not for general // exception handling. DO NOT CHANGE IT to use pending_exception, since diff --git a/src/hotspot/share/prims/upcallStubs.cpp b/src/hotspot/share/prims/upcallStubs.cpp index 5215e6d1735..a3271589fbb 100644 --- a/src/hotspot/share/prims/upcallStubs.cpp +++ b/src/hotspot/share/prims/upcallStubs.cpp @@ -26,6 +26,7 @@ #include "runtime/interfaceSupport.inline.hpp" JVM_ENTRY(static jboolean, UH_FreeUpcallStub0(JNIEnv *env, jobject _unused, jlong addr)) + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); // safe to call 'find_blob' without code cache lock, because stub is always alive CodeBlob* cb = CodeCache::find_blob((char*)addr); if (cb == nullptr) { diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index e2029a26d37..2beba9abb06 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -132,6 +132,7 @@ void DeoptimizationScope::mark(nmethod* nm, bool inc_recompile_counts) { return; } + MACOS_AARCH64_ONLY(os::thread_wx_enable_write()); nmethod::DeoptimizationStatus status = inc_recompile_counts ? nmethod::deoptimize : nmethod::deoptimize_noupdate; AtomicAccess::store(&nm->_deoptimization_status, status); diff --git a/src/hotspot/share/runtime/interfaceSupport.inline.hpp b/src/hotspot/share/runtime/interfaceSupport.inline.hpp index ecd7397d81a..cd3a45a8d3c 100644 --- a/src/hotspot/share/runtime/interfaceSupport.inline.hpp +++ b/src/hotspot/share/runtime/interfaceSupport.inline.hpp @@ -279,12 +279,14 @@ class VMNativeEntryWrapper { os::verify_stack_alignment(); \ /* begin of body */ - #define JRT_ENTRY(result_type, header) \ result_type header { \ assert(current == JavaThread::current(), "Must be"); \ - MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \ ThreadInVMfromJava __tiv(current); \ + MACOS_AARCH64_ONLY( \ + static WXMode wx_mode = DefaultWXWriteMode; \ + ThreadWXEnable __wx(&wx_mode, current); \ + ) \ VM_ENTRY_BASE(result_type, header, current) \ DEBUG_ONLY(VMEntryWrapper __vew;) @@ -311,8 +313,11 @@ class VMNativeEntryWrapper { #define JRT_ENTRY_NO_ASYNC(result_type, header) \ result_type header { \ assert(current == JavaThread::current(), "Must be"); \ - MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \ ThreadInVMfromJava __tiv(current, false /* check asyncs */); \ + MACOS_AARCH64_ONLY( \ + static WXMode wx_mode = DefaultWXWriteMode; \ + ThreadWXEnable __wx(&wx_mode, current); \ + ) \ VM_ENTRY_BASE(result_type, header, current) \ DEBUG_ONLY(VMEntryWrapper __vew;) @@ -321,7 +326,10 @@ class VMNativeEntryWrapper { #define JRT_BLOCK_ENTRY(result_type, header) \ result_type header { \ assert(current == JavaThread::current(), "Must be"); \ - MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current)); \ + MACOS_AARCH64_ONLY( \ + static WXMode wx_mode = DefaultWXWriteMode; \ + ThreadWXEnable __wx(&wx_mode, current); \ + ) \ HandleMarkCleaner __hm(current); #define JRT_BLOCK \ @@ -358,8 +366,11 @@ extern "C" { \ result_type JNICALL header { \ JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ assert(thread == Thread::current(), "JNIEnv is only valid in same thread"); \ - MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \ ThreadInVMfromNative __tiv(thread); \ + MACOS_AARCH64_ONLY( \ + static WXMode wx_mode = DefaultWXWriteMode; \ + ThreadWXEnable __wx(&wx_mode, thread); \ + ) \ DEBUG_ONLY(VMNativeEntryWrapper __vew;) \ VM_ENTRY_BASE(result_type, header, thread) @@ -383,8 +394,11 @@ extern "C" { \ extern "C" { \ result_type JNICALL header { \ JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ - MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \ ThreadInVMfromNative __tiv(thread); \ + MACOS_AARCH64_ONLY( \ + static WXMode wx_mode = DefaultWXWriteMode; \ + ThreadWXEnable __wx(&wx_mode, thread); \ + ) \ DEBUG_ONLY(VMNativeEntryWrapper __vew;) \ VM_ENTRY_BASE(result_type, header, thread) @@ -393,8 +407,11 @@ extern "C" { \ extern "C" { \ result_type JNICALL header { \ JavaThread* thread = JavaThread::current(); \ - MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \ ThreadInVMfromNative __tiv(thread); \ + MACOS_AARCH64_ONLY( \ + static WXMode wx_mode = DefaultWXWriteMode; \ + ThreadWXEnable __wx(&wx_mode, thread); \ + ) \ DEBUG_ONLY(VMNativeEntryWrapper __vew;) \ VM_ENTRY_BASE(result_type, header, thread) diff --git a/src/hotspot/share/runtime/javaCalls.cpp b/src/hotspot/share/runtime/javaCalls.cpp index 3ed678284e4..da701640006 100644 --- a/src/hotspot/share/runtime/javaCalls.cpp +++ b/src/hotspot/share/runtime/javaCalls.cpp @@ -94,16 +94,12 @@ JavaCallWrapper::JavaCallWrapper(const methodHandle& callee_method, Handle recei DEBUG_ONLY(_thread->inc_java_call_counter()); _thread->set_active_handles(new_handles); // install new handle block and reset Java frame linkage - - MACOS_AARCH64_ONLY(_thread->enable_wx(WXExec)); } JavaCallWrapper::~JavaCallWrapper() { assert(_thread == JavaThread::current(), "must still be the same thread"); - MACOS_AARCH64_ONLY(_thread->enable_wx(WXWrite)); - // restore previous handle block & Java frame linkage JNIHandleBlock *_old_handles = _thread->active_handles(); _thread->set_active_handles(_handles); @@ -413,17 +409,20 @@ void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaC #endif } } - StubRoutines::call_stub()( - (address)&link, - // (intptr_t*)&(result->_value), // see NOTE above (compiler problem) - result_val_address, // see NOTE above (compiler problem) - result_type, - method(), - entry_point, - parameter_address, - args->size_of_parameters(), - CHECK - ); + { + MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXExec, thread)); + StubRoutines::call_stub()( + (address)&link, + // (intptr_t*)&(result->_value), // see NOTE above (compiler problem) + result_val_address, // see NOTE above (compiler problem) + result_type, + method(), + entry_point, + parameter_address, + args->size_of_parameters(), + CHECK + ); + } result = link.result(); // circumvent MS C++ 5.0 compiler bug (result is clobbered across call) // Preserve oop return value across possible gc points diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index e73347f35d8..a891e333d4c 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -377,15 +377,6 @@ void JavaThread::check_possible_safepoint() { // Clear unhandled oops in JavaThreads so we get a crash right away. clear_unhandled_oops(); #endif // CHECK_UNHANDLED_OOPS - - // Macos/aarch64 should be in the right state for safepoint (e.g. - // deoptimization needs WXWrite). Crashes caused by the wrong state rarely - // happens in practice, making such issues hard to find and reproduce. -#if defined(__APPLE__) && defined(AARCH64) - if (AssertWXAtThreadSync) { - assert_wx_state(WXWrite); - } -#endif } void JavaThread::check_for_valid_safepoint_state() { @@ -521,6 +512,11 @@ JavaThread::JavaThread(MemTag mem_tag) : _last_freeze_fail_result(freeze_ok), #endif +#ifdef MACOS_AARCH64 + _cur_wx_enable(nullptr), + _cur_wx_mode(0), +#endif + _lock_stack(this), _om_cache(this) { set_jni_functions(jni_functions()); diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp index 1aae37c0697..755a6abe0d8 100644 --- a/src/hotspot/share/runtime/javaThread.hpp +++ b/src/hotspot/share/runtime/javaThread.hpp @@ -81,6 +81,7 @@ class JavaThread; typedef void (*ThreadFunction)(JavaThread*, TRAPS); class EventVirtualThreadPinned; +class ThreadWXEnable; class JavaThread: public Thread { friend class VMStructs; @@ -1288,6 +1289,15 @@ public: bool get_and_clear_interrupted(); private: + +#ifdef MACOS_AARCH64 + friend class ThreadWXEnable; + friend class PosixSignals; + + ThreadWXEnable* _cur_wx_enable; + WXMode* _cur_wx_mode; +#endif + LockStack _lock_stack; OMCache _om_cache; diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 29c872157fd..c6a1d670926 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -140,11 +140,16 @@ enum ThreadPriority { // JLS 20.20.1-3 CriticalPriority = 11 // Critical thread priority }; +#ifdef MACOS_AARCH64 enum WXMode { - WXWrite, - WXExec + WXWrite = 0, + WXExec = 1, + WXArmedForWrite = 2, }; +extern WXMode DefaultWXWriteMode; +#endif // MACOS_AARCH64 + // Executable parameter flag for os::commit_memory() and // os::commit_memory_or_exit(). const bool ExecMem = true; @@ -1128,9 +1133,23 @@ class os: AllStatic { static char* build_agent_function_name(const char *sym, const char *cname, bool is_absolute_path); -#if defined(__APPLE__) && defined(AARCH64) +#ifdef MACOS_AARCH64 // Enables write or execute access to writeable and executable pages. static void current_thread_enable_wx(WXMode mode); + // Macos-AArch64 only. + static void thread_wx_enable_write_impl(); + + // Short circuit write enabling if it's already enabled. This + // function is executed many times, so it makes sense to inline a + // small part of it. +private: + static THREAD_LOCAL bool _jit_exec_enabled; +public: + static void thread_wx_enable_write() { + if (__builtin_expect(_jit_exec_enabled, false)) { + thread_wx_enable_write_impl(); + } + } #endif // __APPLE__ && AARCH64 protected: diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 29e3c07ba20..dc09d4c68c2 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -602,18 +602,21 @@ protected: jint _hashStateY; jint _hashStateZ; -#if defined(__APPLE__) && defined(AARCH64) +#ifdef MACOS_AARCH64 private: DEBUG_ONLY(bool _wx_init); WXMode _wx_state; public: void init_wx(); WXMode enable_wx(WXMode new_state); - + bool wx_enable_write(); void assert_wx_state(WXMode expected) { assert(_wx_state == expected, "wrong state"); } -#endif // __APPLE__ && AARCH64 + WXMode get_wx_state() { + return _wx_state; + } +#endif // MACOS_AARCH64 private: bool _in_asgct = false; diff --git a/src/hotspot/share/runtime/thread.inline.hpp b/src/hotspot/share/runtime/thread.inline.hpp index 61866674224..b194f5e2a7f 100644 --- a/src/hotspot/share/runtime/thread.inline.hpp +++ b/src/hotspot/share/runtime/thread.inline.hpp @@ -30,8 +30,9 @@ #include "gc/shared/tlab_globals.hpp" #include "runtime/atomicAccess.hpp" +#include "utilities/permitForbiddenFunctions.hpp" -#if defined(__APPLE__) && defined(AARCH64) +#ifdef MACOS_AARCH64 #include "runtime/os.hpp" #endif @@ -60,11 +61,17 @@ inline void Thread::set_threads_hazard_ptr(ThreadsList* new_list) { } #if defined(__APPLE__) && defined(AARCH64) + +static void dummy() { } + inline void Thread::init_wx() { assert(this == Thread::current(), "should only be called for current thread"); assert(!_wx_init, "second init"); _wx_state = WXWrite; + permit_forbidden_function::pthread_jit_write_protect_np(false); os::current_thread_enable_wx(_wx_state); + // Side effect: preload base address of libjvm + guarantee(os::address_is_in_vm(CAST_FROM_FN_PTR(address, &dummy)), "must be"); DEBUG_ONLY(_wx_init = true); } @@ -74,10 +81,19 @@ inline WXMode Thread::enable_wx(WXMode new_state) { WXMode old = _wx_state; if (_wx_state != new_state) { _wx_state = new_state; - os::current_thread_enable_wx(new_state); + switch (new_state) { + case WXWrite: + case WXExec: + os::current_thread_enable_wx(new_state); + break; + case WXArmedForWrite: + break; + default: ShouldNotReachHere(); break; + } } return old; } + #endif // __APPLE__ && AARCH64 #endif // SHARE_RUNTIME_THREAD_INLINE_HPP diff --git a/src/hotspot/share/runtime/threadWXSetters.inline.hpp b/src/hotspot/share/runtime/threadWXSetters.inline.hpp index 121584b81be..e7e37fcde1b 100644 --- a/src/hotspot/share/runtime/threadWXSetters.inline.hpp +++ b/src/hotspot/share/runtime/threadWXSetters.inline.hpp @@ -28,25 +28,62 @@ // No threadWXSetters.hpp -#if defined(__APPLE__) && defined(AARCH64) +#ifdef MACOS_AARCH64 +#include "classfile/classLoader.hpp" +#include "runtime/perfData.inline.hpp" #include "runtime/thread.inline.hpp" class ThreadWXEnable { Thread* _thread; WXMode _old_mode; + WXMode *_this_wx_mode; + ThreadWXEnable *_prev; public: - ThreadWXEnable(WXMode new_mode, Thread* thread) : - _thread(thread), - _old_mode(_thread ? _thread->enable_wx(new_mode) : WXWrite) - { } - ~ThreadWXEnable() { - if (_thread) { - _thread->enable_wx(_old_mode); + ThreadWXEnable(WXMode* new_mode, Thread* thread) : + _thread(thread), _this_wx_mode(new_mode) { + NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());) + JavaThread* javaThread + = _thread && _thread->is_Java_thread() + ? JavaThread::cast(_thread) : nullptr; + _prev = javaThread != nullptr ? javaThread->_cur_wx_enable: nullptr; + _old_mode = _thread != nullptr ? _thread->enable_wx(*new_mode) : WXWrite; + if (javaThread != nullptr) { + javaThread->_cur_wx_enable = this; + javaThread->_cur_wx_mode = new_mode; } } + ThreadWXEnable(WXMode new_mode, Thread* thread) : + _thread(thread), _this_wx_mode(nullptr) { + NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());) + JavaThread* javaThread + = _thread && _thread->is_Java_thread() + ? JavaThread::cast(_thread) : nullptr; + _prev = javaThread != nullptr ? javaThread->_cur_wx_enable: nullptr; + _old_mode = _thread != nullptr ? _thread->enable_wx(new_mode) : WXWrite; + if (javaThread) { + javaThread->_cur_wx_enable = this; + javaThread->_cur_wx_mode = nullptr; + } + } + + ~ThreadWXEnable() { + NOT_PRODUCT(PerfTraceTime ptt(ClassLoader::perf_change_wx_time());) + if (_thread) { + _thread->enable_wx(_old_mode); + JavaThread* javaThread + = _thread && _thread->is_Java_thread() + ? JavaThread::cast(_thread) : nullptr; + if (javaThread != nullptr) { + javaThread->_cur_wx_enable = _prev; + javaThread->_cur_wx_mode = _prev != nullptr ? _prev->_this_wx_mode : nullptr; + } + } + } + + static bool test(address p); }; -#endif // __APPLE__ && AARCH64 +#endif // MACOS_AARCH64 #endif // SHARE_RUNTIME_THREADWXSETTERS_INLINE_HPP diff --git a/src/hotspot/share/utilities/forbiddenFunctions.hpp b/src/hotspot/share/utilities/forbiddenFunctions.hpp index 9d1b88e6233..9b3c65b0656 100644 --- a/src/hotspot/share/utilities/forbiddenFunctions.hpp +++ b/src/hotspot/share/utilities/forbiddenFunctions.hpp @@ -63,4 +63,8 @@ PRAGMA_DIAG_POP FORBID_IMPORTED_C_FUNCTION(char* strdup(const char *s), noexcept, "use os::strdup"); FORBID_IMPORTED_C_FUNCTION(wchar_t* wcsdup(const wchar_t *s), noexcept, "don't use"); +// Disallow non-wrapped raw library function. +MACOS_AARCH64_ONLY(FORBID_C_FUNCTION(void pthread_jit_write_protect_np(int enabled), noexcept, \ + "use os::current_thread_enable_wx");) + #endif // SHARE_UTILITIES_FORBIDDENFUNCTIONS_HPP diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index 8404fc757f0..a03255b5cf3 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -548,6 +548,9 @@ #endif #define MACOS_AARCH64_ONLY(x) MACOS_ONLY(AARCH64_ONLY(x)) +#if defined(__APPLE__) && defined(AARCH64) +#define MACOS_AARCH64 1 +#endif #if defined(RISCV32) || defined(RISCV64) #define RISCV diff --git a/src/hotspot/share/utilities/permitForbiddenFunctions.hpp b/src/hotspot/share/utilities/permitForbiddenFunctions.hpp index 71719ac8a76..5dec5062b0c 100644 --- a/src/hotspot/share/utilities/permitForbiddenFunctions.hpp +++ b/src/hotspot/share/utilities/permitForbiddenFunctions.hpp @@ -70,6 +70,10 @@ inline void* realloc(void* ptr, size_t size) { return ::realloc(ptr, size); } inline char* strdup(const char* s) { return ::strdup(s); } +MACOS_AARCH64_ONLY( \ + inline void pthread_jit_write_protect_np(int enabled) { return ::pthread_jit_write_protect_np(enabled); } \ +) + END_ALLOW_FORBIDDEN_FUNCTIONS } // namespace permit_forbidden_function diff --git a/test/hotspot/jtreg/runtime/os/TestWXHealing.java b/test/hotspot/jtreg/runtime/os/TestWXHealing.java new file mode 100644 index 00000000000..46875848a89 --- /dev/null +++ b/test/hotspot/jtreg/runtime/os/TestWXHealing.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 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. + */ + +/* + * @test + * @requires os.family == "mac" + * @requires os.arch == "aarch64" + * @summary Run shell with -XX:+StressWXHealing. This tests most of + * the triggers for WX mode. + * @library /test/lib + * @compile WXHealing.java + * @run main TestWXHealing + */ + +import java.util.regex.*; +import jdk.test.lib.process.*; + +import static java.nio.charset.StandardCharsets.*; + +public class TestWXHealing { + + public static void main(String[] args) throws Throwable { + String[] opts = {"-XX:+UnlockDiagnosticVMOptions", + "-XX:+TraceWXHealing", "-XX:+StressWXHealing", "WXHealing"}; + var process = ProcessTools.createTestJavaProcessBuilder(opts).start(); + String output = new String(process.getInputStream().readAllBytes(), UTF_8); + System.out.println(output); + if (output.contains("MAP_JIT write protection does not work on this system")) { + System.out.println("Test was not run because MAP_JIT write protection does not work on this system"); + } else { + var pattern = Pattern.compile("Healing WXMode WXArmedForWrite at 0x[0-9a-f]* to WXWrite "); + var matches = pattern.matcher(output).results().count(); + if (matches < 10) { + throw new RuntimeException("Only " + matches + " healings in\n" + output); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/os/WXHealing.java b/test/hotspot/jtreg/runtime/os/WXHealing.java new file mode 100644 index 00000000000..ba790db2c59 --- /dev/null +++ b/test/hotspot/jtreg/runtime/os/WXHealing.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 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. + */ + +import java.io.*; +import jdk.jshell.tool.*; + +public class WXHealing { + + // There's nothing special about jshell here: we just need an + // application that does a lot of compilation and class loading. + public static void main(String[] args) throws Throwable { + JavaShellToolBuilder + .builder() + .in(new ByteArrayInputStream + (""" + void main() { + System.out.println("Hello, World!"); + } + main() + 2+2 + Math.sqrt(2) + 4 * Math.atan(1) + Math.exp(1) + """ + .getBytes()), null) + .start(); + } +} From 9f13ec1ccb684398e311b5f139773ca9f39561fe Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Fri, 6 Feb 2026 14:15:09 +0000 Subject: [PATCH 158/215] 8377365: [BACKOUT] Mixed jstack cannot find function in vDSO Reviewed-by: thartmann --- .../linux/native/libsaproc/libproc_impl.h | 5 +- .../linux/native/libsaproc/ps_core.c | 55 +---------- .../sa/LingeredAppWithVDSOCall.java | 61 ------------ .../TestJhsdbJstackMixedWithVDSOCallCore.java | 96 ------------------- test/lib/jdk/test/lib/apps/LingeredApp.java | 16 +--- 5 files changed, 8 insertions(+), 225 deletions(-) delete mode 100644 test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java delete mode 100644 test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h index 262e99f4a64..42a6212510c 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,9 +95,6 @@ struct core_data { // part of the class sharing workaround int classes_jsa_fd; // file descriptor of class share archive uintptr_t dynamic_addr; // address of dynamic section of a.out - uintptr_t vdso_addr; // address of vDSO - off64_t vdso_offset; // offset of vDSO in core - size_t vdso_size; // size of vDSO uintptr_t ld_base_addr; // base address of ld.so size_t num_maps; // number of maps. map_info* maps; // maps in a linked list diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c index 61bd1e80005..899d42152d1 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -31,9 +31,6 @@ #include #include #include -#include -#include -#include #include "libproc_impl.h" #include "ps_core_common.h" #include "proc_service.h" @@ -288,8 +285,6 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { // We will adjust it in read_exec_segments(). ph->core->dynamic_addr = auxv->a_un.a_val; break; - } else if (auxv->a_type == AT_SYSINFO_EHDR) { - ph->core->vdso_addr = auxv->a_un.a_val; } auxv++; } @@ -355,10 +350,6 @@ static bool read_core_segments(struct ps_prochandle* ph, ELF_EHDR* core_ehdr) { print_error("failed to add map info\n"); goto err; } - if (core_php->p_vaddr == ph->core->vdso_addr) { - ph->core->vdso_offset = core_php->p_offset; - ph->core->vdso_size = core_php->p_memsz; - } } break; } @@ -602,39 +593,6 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f return load_addr; } -// Check for vDSO binary in kernel directory (/lib/modules//vdso), -// rewrite the given lib_name string if found. -// Otherwise copy vDSO memory in coredump to temporal memory generated by -// memfd_create(). -// Returns FD for vDSO (should be closed by caller), or -1 on error. -static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name_len) { - int lib_fd; - struct utsname uts; - uname(&uts); - - // Check vDSO binary first (for referring debuginfo if possible). - char *vdso_path = (char*)malloc(lib_name_len); - snprintf(vdso_path, lib_name_len, "/lib/modules/%s/vdso/vdso64.so", uts.release); - lib_fd = pathmap_open(vdso_path); - if (lib_fd != -1) { - print_debug("replace vDSO: %s -> %s\n", lib_name, vdso_path); - strncpy(lib_name, vdso_path, lib_name_len); - } else { - // Copy vDSO memory segment from core to temporal memory - // if vDSO binary is not available. - lib_fd = memfd_create("[vdso] in core", 0); - off64_t ofs = ph->core->vdso_offset; - if (sendfile64(lib_fd, ph->core->core_fd, &ofs, ph->core->vdso_size) == -1) { - print_debug("can't copy vDSO (%d)\n", errno); - close(lib_fd); - lib_fd = -1; - } - } - - free(vdso_path); - return lib_fd; -} - // read shared library info from runtime linker's data structures. // This work is done by librtlb_db in Solaris static bool read_shared_lib_info(struct ps_prochandle* ph) { @@ -729,14 +687,9 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { // it will fail later. } - if (lib_name[0] != '\0') { // ignore empty lib names - // We can use lib_base_diff to compare with vdso_addr - // because base address of vDSO should be 0. - if (lib_base_diff == ph->core->vdso_addr) { - lib_fd = handle_vdso(ph, lib_name, sizeof(lib_name)); - } else { - lib_fd = pathmap_open(lib_name); - } + if (lib_name[0] != '\0') { + // ignore empty lib names + lib_fd = pathmap_open(lib_name); if (lib_fd < 0) { print_debug("can't open shared object %s\n", lib_name); diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java deleted file mode 100644 index d893ddf6f89..00000000000 --- a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java +++ /dev/null @@ -1,61 +0,0 @@ - -/* - * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2026, NTT DATA - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.lang.invoke.MethodHandle; -import java.lang.foreign.FunctionDescriptor; -import java.lang.foreign.Linker; -import java.lang.foreign.ValueLayout; - -import jdk.test.lib.Asserts; -import jdk.test.lib.apps.LingeredApp; - - -public class LingeredAppWithVDSOCall extends LingeredApp { - - private static final MethodHandle gettimeofday; - - static { - var desc = FunctionDescriptor.of(ValueLayout.JAVA_INT, // return - ValueLayout.JAVA_LONG, // tv - ValueLayout.JAVA_LONG); // tz - var linker = Linker.nativeLinker(); - var gettimeofdayPtr = linker.defaultLookup().findOrThrow("gettimeofday"); - gettimeofday = linker.downcallHandle(gettimeofdayPtr, desc); - } - - private static void crashAtGettimeofday(long tvAddr, long tzAddr) { - try { - gettimeofday.invoke(tvAddr, tzAddr); - } catch (Throwable t) { - throw new RuntimeException(t); - } - Asserts.fail("gettimeofday() didn't crash"); - } - - public static void main(String[] args) { - setCrasher(() -> crashAtGettimeofday(100L, 200L)); - LingeredApp.main(args); - } -} diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java deleted file mode 100644 index 84da46e272f..00000000000 --- a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2026, NTT DATA - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -import java.nio.file.Path; - -import jtreg.SkippedException; - -import jdk.test.lib.JDKToolFinder; -import jdk.test.lib.JDKToolLauncher; -import jdk.test.lib.Platform; -import jdk.test.lib.SA.SATestUtils; -import jdk.test.lib.Utils; -import jdk.test.lib.apps.LingeredApp; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.util.CoreUtils; - -/** - * @test - * @bug 8376269 - * @requires (os.family == "linux") & (vm.hasSA) - * @requires os.arch == "amd64" - * @library /test/lib - * @run driver TestJhsdbJstackMixedWithVDSOCallCore - */ -public class TestJhsdbJstackMixedWithVDSOCallCore { - - private static void runJstackMixed(String coreFileName) throws Exception { - JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); - launcher.addVMArgs(Utils.getTestJavaOpts()); - launcher.addToolArg("jstack"); - launcher.addToolArg("--mixed"); - launcher.addToolArg("--exe"); - launcher.addToolArg(JDKToolFinder.getTestJDKTool("java")); - launcher.addToolArg("--core"); - launcher.addToolArg(coreFileName); - - ProcessBuilder pb = SATestUtils.createProcessBuilder(launcher); - Process jhsdb = pb.start(); - OutputAnalyzer out = new OutputAnalyzer(jhsdb); - - jhsdb.waitFor(); - - System.out.println(out.getStdout()); - System.err.println(out.getStderr()); - - out.shouldContain("vdso_gettimeofday"); - } - - private static void checkVDSODebugInfo() { - var kernelVersion = System.getProperty("os.version"); - var vdso = Path.of("/lib", "modules", kernelVersion, "vdso", "vdso64.so"); - if (SATestUtils.getDebugInfo(vdso.toString()) == null) { - // Skip this test if debuginfo of vDSO not found because internal - // function of gettimeofday() would not be exported, and vDSO - // binary might be stripped. - throw new SkippedException("vDSO debuginfo not found (" + vdso.toString() + ")"); - } - } - - public static void main(String... args) throws Throwable { - if (Platform.isMusl()) { - throw new SkippedException("This test does not work on musl libc."); - } - checkVDSODebugInfo(); - - var app = new LingeredAppWithVDSOCall(); - app.setForceCrash(true); - LingeredApp.startApp(app, CoreUtils.getAlwaysPretouchArg(true)); - app.waitAppTerminate(); - - String crashOutput = app.getOutput().getStdout(); - String coreFileName = CoreUtils.getCoreFileLocation(crashOutput, app.getPid()); - runJstackMixed(coreFileName); - } -} diff --git a/test/lib/jdk/test/lib/apps/LingeredApp.java b/test/lib/jdk/test/lib/apps/LingeredApp.java index 38ad9ae5b0e..13008e68c54 100644 --- a/test/lib/jdk/test/lib/apps/LingeredApp.java +++ b/test/lib/jdk/test/lib/apps/LingeredApp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -122,12 +122,6 @@ public class LingeredApp { this.forceCrash = forceCrash; } - private static Runnable crasher; - - public static void setCrasher(Runnable runnable) { - crasher = runnable; - } - native private static int crash(); /** @@ -634,12 +628,8 @@ public class LingeredApp { synchronized(steadyStateObj) { startSteadyStateThread(steadyStateObj); if (forceCrash) { - if (crasher == null) { - System.loadLibrary("LingeredApp"); // location of native crash() method - crash(); - } else { - crasher.run(); - } + System.loadLibrary("LingeredApp"); // location of native crash() method + crash(); } while (Files.exists(path)) { // Touch the lock to indicate our readiness From cd5256d5a654d436e5ef926f6afb1bcbfc7a8bd1 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 6 Feb 2026 15:19:01 +0000 Subject: [PATCH 159/215] 8374170: I/O Poller updates Reviewed-by: michaelm --- .../sun/nio/ch/DefaultPollerProvider.java | 17 +- .../aix/classes/sun/nio/ch/PollsetPoller.java | 12 +- .../sun/nio/ch/DefaultPollerProvider.java | 34 +- .../linux/classes/sun/nio/ch/EPollPoller.java | 94 ++- .../sun/nio/ch/DefaultPollerProvider.java | 14 +- .../classes/sun/nio/ch/KQueuePoller.java | 98 ++- .../share/classes/java/lang/System.java | 10 +- .../share/classes/java/lang/Thread.java | 11 + .../jdk/internal/access/JavaLangAccess.java | 10 + .../sun/nio/ch/DatagramChannelImpl.java | 16 +- .../share/classes/sun/nio/ch/IOUtil.java | 8 +- .../classes/sun/nio/ch/NativeDispatcher.java | 21 +- .../classes/sun/nio/ch/NativeThreadSet.java | 31 +- .../classes/sun/nio/ch/NioSocketImpl.java | 30 +- .../share/classes/sun/nio/ch/Poller.java | 783 ++++++++++++------ .../classes/sun/nio/ch/PollerProvider.java | 41 +- .../sun/nio/ch/ServerSocketChannelImpl.java | 12 +- .../classes/sun/nio/ch/SocketChannelImpl.java | 38 +- .../unix/classes/sun/nio/ch/NativeThread.java | 58 +- .../classes/sun/nio/ch/SinkChannelImpl.java | 12 +- .../classes/sun/nio/ch/SourceChannelImpl.java | 15 +- .../classes/sun/nio/ch/UnixDispatcher.java | 10 +- src/java.base/unix/native/libnio/ch/IOUtil.c | 7 +- .../sun/nio/ch/DefaultPollerProvider.java | 22 +- .../classes/sun/nio/ch/NativeThread.java | 54 +- .../classes/sun/nio/ch/WEPollPoller.java | 26 +- .../windows/native/libnio/ch/IOUtil.c | 5 +- .../sun/nio/ch/sctp/SctpChannelImpl.java | 28 +- .../sun/nio/ch/sctp/SctpMultiChannelImpl.java | 20 +- .../nio/ch/sctp/SctpServerChannelImpl.java | 14 +- .../java/net/vthread/BlockingSocketOps.java | 1 + .../channels/vthread/BlockingChannelOps.java | 1 + .../nio/channels/vthread/SelectorOps.java | 1 + 33 files changed, 1043 insertions(+), 511 deletions(-) diff --git a/src/java.base/aix/classes/sun/nio/ch/DefaultPollerProvider.java b/src/java.base/aix/classes/sun/nio/ch/DefaultPollerProvider.java index b645b735533..eb895d5a3a1 100644 --- a/src/java.base/aix/classes/sun/nio/ch/DefaultPollerProvider.java +++ b/src/java.base/aix/classes/sun/nio/ch/DefaultPollerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -30,15 +30,28 @@ import java.io.IOException; * Default PollerProvider for AIX. */ class DefaultPollerProvider extends PollerProvider { - DefaultPollerProvider() { } + DefaultPollerProvider(Poller.Mode mode) { + if (mode != Poller.Mode.SYSTEM_THREADS) { + throw new UnsupportedOperationException(); + } + super(mode); + } + + DefaultPollerProvider() { + this(Poller.Mode.SYSTEM_THREADS); + } @Override Poller readPoller(boolean subPoller) throws IOException { + if (subPoller) + throw new UnsupportedOperationException(); return new PollsetPoller(true); } @Override Poller writePoller(boolean subPoller) throws IOException { + if (subPoller) + throw new UnsupportedOperationException(); return new PollsetPoller(false); } } diff --git a/src/java.base/aix/classes/sun/nio/ch/PollsetPoller.java b/src/java.base/aix/classes/sun/nio/ch/PollsetPoller.java index 724f14495a8..3eee39906ee 100644 --- a/src/java.base/aix/classes/sun/nio/ch/PollsetPoller.java +++ b/src/java.base/aix/classes/sun/nio/ch/PollsetPoller.java @@ -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, IBM Corp. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -52,13 +52,19 @@ class PollsetPoller extends Poller { this.pollBuffer = Pollset.allocatePollArray(MAX_EVENTS_TO_POLL); } + @Override + void close() { + Pollset.pollsetDestroy(setid); + Pollset.freePollArray(pollBuffer); + } + @Override int fdVal() { return setid; } @Override - void implRegister(int fd) throws IOException { + void implStartPoll(int fd) throws IOException { int ret = Pollset.pollsetCtl(setid, Pollset.PS_MOD, fd, Pollset.PS_POLLPRI | event); if (ret != 0) { throw new IOException("Unable to register fd " + fd); @@ -66,7 +72,7 @@ class PollsetPoller extends Poller { } @Override - void implDeregister(int fd, boolean polled) { + void implStopPoll(int fd, boolean polled) { int ret = Pollset.pollsetCtl(setid, Pollset.PS_DELETE, fd, 0); assert ret == 0; } diff --git a/src/java.base/linux/classes/sun/nio/ch/DefaultPollerProvider.java b/src/java.base/linux/classes/sun/nio/ch/DefaultPollerProvider.java index a9b169a4657..b53a90e4f7f 100644 --- a/src/java.base/linux/classes/sun/nio/ch/DefaultPollerProvider.java +++ b/src/java.base/linux/classes/sun/nio/ch/DefaultPollerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -31,34 +31,34 @@ import jdk.internal.vm.ContinuationSupport; * Default PollerProvider for Linux. */ class DefaultPollerProvider extends PollerProvider { - DefaultPollerProvider() { } + DefaultPollerProvider(Poller.Mode mode) { + super(mode); + } - @Override - Poller.Mode defaultPollerMode() { - if (ContinuationSupport.isSupported()) { - return Poller.Mode.VTHREAD_POLLERS; - } else { - return Poller.Mode.SYSTEM_THREADS; - } + DefaultPollerProvider() { + var mode = ContinuationSupport.isSupported() + ? Poller.Mode.VTHREAD_POLLERS + : Poller.Mode.SYSTEM_THREADS; + this(mode); } @Override - int defaultReadPollers(Poller.Mode mode) { + int defaultReadPollers() { int ncpus = Runtime.getRuntime().availableProcessors(); - if (mode == Poller.Mode.VTHREAD_POLLERS) { - return Math.min(Integer.highestOneBit(ncpus), 32); - } else { - return Math.max(Integer.highestOneBit(ncpus / 4), 1); - } + return switch (pollerMode()) { + case SYSTEM_THREADS -> Math.max(Integer.highestOneBit(ncpus / 4), 1); + case VTHREAD_POLLERS -> Math.min(Integer.highestOneBit(ncpus), 32); + default -> super.defaultReadPollers(); + }; } @Override Poller readPoller(boolean subPoller) throws IOException { - return new EPollPoller(subPoller, true); + return new EPollPoller(pollerMode(), subPoller, true); } @Override Poller writePoller(boolean subPoller) throws IOException { - return new EPollPoller(subPoller, false); + return new EPollPoller(pollerMode(), subPoller, false); } } diff --git a/src/java.base/linux/classes/sun/nio/ch/EPollPoller.java b/src/java.base/linux/classes/sun/nio/ch/EPollPoller.java index cdebff7c766..5c45393bd62 100644 --- a/src/java.base/linux/classes/sun/nio/ch/EPollPoller.java +++ b/src/java.base/linux/classes/sun/nio/ch/EPollPoller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,6 +25,8 @@ package sun.nio.ch; import java.io.IOException; +import java.lang.ref.Cleaner.Cleanable; +import jdk.internal.ref.CleanerFactory; import static sun.nio.ch.EPoll.*; /** @@ -38,12 +40,70 @@ class EPollPoller extends Poller { private final int event; private final int maxEvents; private final long address; + private final EventFD eventfd; // wakeup event, used for shutdown - EPollPoller(boolean subPoller, boolean read) throws IOException { - this.epfd = EPoll.create(); + // close action, and cleaner if this is subpoller + private final Runnable closer; + private final Cleanable cleaner; + + EPollPoller(Poller.Mode mode, boolean subPoller, boolean read) throws IOException { + boolean wakeable = (mode == Mode.POLLER_PER_CARRIER) && subPoller; + int maxEvents = (subPoller) ? 16 : 64; + + int epfd = EPoll.create(); + long address = 0L; + EventFD eventfd = null; + try { + address = EPoll.allocatePollArray(maxEvents); + + // register one end of the pipe with epoll to allow for wakeup + if (wakeable) { + eventfd = new EventFD(); + IOUtil.configureBlocking(eventfd.efd(), false); + EPoll.ctl(epfd, EPOLL_CTL_ADD, eventfd.efd(), EPOLLIN); + } + } catch (Throwable e) { + FileDispatcherImpl.closeIntFD(epfd); + if (address != 0L) EPoll.freePollArray(address); + if (eventfd != null) eventfd.close(); + throw e; + } + + this.epfd = epfd; this.event = (read) ? EPOLLIN : EPOLLOUT; - this.maxEvents = (subPoller) ? 64 : 512; - this.address = EPoll.allocatePollArray(maxEvents); + this.maxEvents = maxEvents; + this.address = address; + this.eventfd = eventfd; + + // create action to close epoll instance, register cleaner when wakeable + this.closer = closer(epfd, address, eventfd); + if (wakeable) { + this.cleaner = CleanerFactory.cleaner().register(this, closer); + } else { + this.cleaner = null; + } + } + + /** + * Returns an action to close the epoll instance and release other resources. + */ + private static Runnable closer(int epfd, long address, EventFD eventfd) { + return () -> { + try { + FileDispatcherImpl.closeIntFD(epfd); + EPoll.freePollArray(address); + if (eventfd != null) eventfd.close(); + } catch (IOException _) { } + }; + } + + @Override + void close() { + if (cleaner != null) { + cleaner.clean(); + } else { + closer.run(); + } } @Override @@ -52,8 +112,8 @@ class EPollPoller extends Poller { } @Override - void implRegister(int fdVal) throws IOException { - // re-arm + void implStartPoll(int fdVal) throws IOException { + // re-enable if already registered but disabled (previously polled) int err = EPoll.ctl(epfd, EPOLL_CTL_MOD, fdVal, (event | EPOLLONESHOT)); if (err == ENOENT) err = EPoll.ctl(epfd, EPOLL_CTL_ADD, fdVal, (event | EPOLLONESHOT)); @@ -62,24 +122,36 @@ class EPollPoller extends Poller { } @Override - void implDeregister(int fdVal, boolean polled) { + void implStopPoll(int fdVal, boolean polled) { // event is disabled if already polled if (!polled) { EPoll.ctl(epfd, EPOLL_CTL_DEL, fdVal, 0); } } + @Override + void wakeupPoller() throws IOException { + if (eventfd == null) { + throw new UnsupportedOperationException(); + } + eventfd.set(); + } + @Override int poll(int timeout) throws IOException { int n = EPoll.wait(epfd, address, maxEvents, timeout); + int polled = 0; int i = 0; while (i < n) { long eventAddress = EPoll.getEvent(address, i); - int fdVal = EPoll.getDescriptor(eventAddress); - polled(fdVal); + int fd = EPoll.getDescriptor(eventAddress); + if (eventfd == null || fd != eventfd.efd()) { + polled(fd); + polled++; + } i++; } - return n; + return polled; } } diff --git a/src/java.base/macosx/classes/sun/nio/ch/DefaultPollerProvider.java b/src/java.base/macosx/classes/sun/nio/ch/DefaultPollerProvider.java index dc32c2cd90c..6349ae503e4 100644 --- a/src/java.base/macosx/classes/sun/nio/ch/DefaultPollerProvider.java +++ b/src/java.base/macosx/classes/sun/nio/ch/DefaultPollerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -30,15 +30,21 @@ import java.io.IOException; * Default PollerProvider for macOS. */ class DefaultPollerProvider extends PollerProvider { - DefaultPollerProvider() { } + DefaultPollerProvider(Poller.Mode mode) { + super(mode); + } + + DefaultPollerProvider() { + this(Poller.Mode.SYSTEM_THREADS); + } @Override Poller readPoller(boolean subPoller) throws IOException { - return new KQueuePoller(subPoller, true); + return new KQueuePoller(pollerMode(), subPoller, true); } @Override Poller writePoller(boolean subPoller) throws IOException { - return new KQueuePoller(subPoller, false); + return new KQueuePoller(pollerMode(), subPoller, false); } } diff --git a/src/java.base/macosx/classes/sun/nio/ch/KQueuePoller.java b/src/java.base/macosx/classes/sun/nio/ch/KQueuePoller.java index 6a1c771820e..69c191913a9 100644 --- a/src/java.base/macosx/classes/sun/nio/ch/KQueuePoller.java +++ b/src/java.base/macosx/classes/sun/nio/ch/KQueuePoller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,6 +25,8 @@ package sun.nio.ch; import java.io.IOException; +import java.lang.ref.Cleaner.Cleanable; +import jdk.internal.ref.CleanerFactory; import static sun.nio.ch.KQueue.*; /** @@ -36,11 +38,77 @@ class KQueuePoller extends Poller { private final int maxEvents; private final long address; - KQueuePoller(boolean subPoller, boolean read) throws IOException { - this.kqfd = KQueue.create(); + // file descriptors used for wakeup during shutdown + private final int fd0; + private final int fd1; + + // close action, and cleaner if this is subpoller + private final Runnable closer; + private final Cleanable cleaner; + + KQueuePoller(Poller.Mode mode, boolean subPoller, boolean read) throws IOException { + boolean wakeable = (mode == Mode.POLLER_PER_CARRIER) && subPoller; + int maxEvents = (subPoller) ? 16 : 64; + + int kqfd = KQueue.create(); + long address = 0L; + int fd0 = -1; + int fd1 = -1; + try { + address = KQueue.allocatePollArray(maxEvents); + + // register one end of the pipe with kqueue to allow for wakeup + if (wakeable) { + long fds = IOUtil.makePipe(false); + fd0 = (int) (fds >>> 32); + fd1 = (int) fds; + KQueue.register(kqfd, fd0, EVFILT_READ, EV_ADD); + } + } catch (Throwable e) { + FileDispatcherImpl.closeIntFD(kqfd); + if (address != 0L) KQueue.freePollArray(address); + if (fd0 >= 0) FileDispatcherImpl.closeIntFD(fd0); + if (fd1 >= 0) FileDispatcherImpl.closeIntFD(fd1); + throw e; + } + + this.kqfd = kqfd; this.filter = (read) ? EVFILT_READ : EVFILT_WRITE; - this.maxEvents = (subPoller) ? 64 : 512; - this.address = KQueue.allocatePollArray(maxEvents); + this.maxEvents = maxEvents; + this.address = address; + this.fd0 = fd0; + this.fd1 = fd1; + + // create action to close kqueue, register cleaner when wakeable + this.closer = closer(kqfd, address, fd0, fd1); + if (wakeable) { + this.cleaner = CleanerFactory.cleaner().register(this, closer); + } else { + this.cleaner = null; + } + } + + /** + * Returns an action to close the kqueue and release other resources. + */ + private static Runnable closer(int kqfd, long address, int fd0, int fd1) { + return () -> { + try { + FileDispatcherImpl.closeIntFD(kqfd); + KQueue.freePollArray(address); + if (fd0 >= 0) FileDispatcherImpl.closeIntFD(fd0); + if (fd1 >= 0) FileDispatcherImpl.closeIntFD(fd1); + } catch (IOException _) { } + }; + } + + @Override + void close() { + if (cleaner != null) { + cleaner.clean(); + } else { + closer.run(); + } } @Override @@ -49,30 +117,42 @@ class KQueuePoller extends Poller { } @Override - void implRegister(int fdVal) throws IOException { + void implStartPoll(int fdVal) throws IOException { int err = KQueue.register(kqfd, fdVal, filter, (EV_ADD|EV_ONESHOT)); if (err != 0) throw new IOException("kevent failed: " + err); } @Override - void implDeregister(int fdVal, boolean polled) { + void implStopPoll(int fdVal, boolean polled) { // event was deleted if already polled if (!polled) { KQueue.register(kqfd, fdVal, filter, EV_DELETE); } } + @Override + void wakeupPoller() throws IOException { + if (fd1 < 0) { + throw new UnsupportedOperationException(); + } + IOUtil.write1(fd1, (byte)0); + } + @Override int poll(int timeout) throws IOException { int n = KQueue.poll(kqfd, address, maxEvents, timeout); + int polled = 0; int i = 0; while (i < n) { long keventAddress = KQueue.getEvent(address, i); int fdVal = KQueue.getDescriptor(keventAddress); - polled(fdVal); + if (fdVal != fd0) { + polled(fdVal); + polled++; + } i++; } - return n; + return polled; } } diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index bd684fab629..2f772e4d065 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -2277,6 +2277,14 @@ public final class System { return Thread.scopedValueBindings(); } + public long nativeThreadID(Thread thread) { + return thread.nativeThreadID(); + } + + public void setThreadNativeID(long id) { + Thread.currentThread().setNativeThreadID(id); + } + public Continuation getContinuation(Thread thread) { return thread.getContinuation(); } @@ -2311,7 +2319,7 @@ public final class System { if (thread instanceof BaseVirtualThread vthread) { vthread.unpark(); } else { - throw new WrongThreadException(); + throw new IllegalArgumentException(); } } diff --git a/src/java.base/share/classes/java/lang/Thread.java b/src/java.base/share/classes/java/lang/Thread.java index bb1292b374a..57d28aca5f4 100644 --- a/src/java.base/share/classes/java/lang/Thread.java +++ b/src/java.base/share/classes/java/lang/Thread.java @@ -286,6 +286,9 @@ public class Thread implements Runnable { volatile boolean daemon; volatile int threadStatus; + // Used by NativeThread for signalling + @Stable long nativeThreadID; + // This map is maintained by the ThreadLocal class ThreadLocal.ThreadLocalMap terminatingThreadLocals; @@ -312,6 +315,14 @@ public class Thread implements Runnable { holder.terminatingThreadLocals = map; } + long nativeThreadID() { + return holder.nativeThreadID; + } + + void setNativeThreadID(long id) { + holder.nativeThreadID = id; + } + /* * ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 4ae1905aa10..9d980c3ba3b 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -581,6 +581,16 @@ public interface JavaLangAccess { */ Object scopedValueBindings(); + /** + * Returns the native thread ID for the given platform thread or 0 if not set. + */ + long nativeThreadID(Thread thread); + + /** + * Sets the native thread ID for the current platform thread. + */ + void setThreadNativeID(long id); + /** * Returns the innermost mounted continuation */ diff --git a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java index afb312ed722..f5002e8b716 100644 --- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -134,9 +134,9 @@ class DatagramChannelImpl private static final int ST_CLOSED = 3; private int state; - // IDs of native threads doing reads and writes, for signalling - private long readerThread; - private long writerThread; + // Threads doing reads and writes, for signalling + private Thread readerThread; + private Thread writerThread; // Local and remote (connected) address private InetSocketAddress localAddress; @@ -523,7 +523,7 @@ class DatagramChannelImpl if (localAddress == null) bindInternal(null); if (blocking) - readerThread = NativeThread.current(); + readerThread = NativeThread.threadToSignal(); } return remote; } @@ -538,7 +538,7 @@ class DatagramChannelImpl { if (blocking) { synchronized (stateLock) { - readerThread = 0; + readerThread = null; if (state == ST_CLOSING) { tryFinishClose(); } @@ -1030,7 +1030,7 @@ class DatagramChannelImpl if (localAddress == null) bindInternal(null); if (blocking) - writerThread = NativeThread.current(); + writerThread = NativeThread.threadToSignal(); } return remote; } @@ -1045,7 +1045,7 @@ class DatagramChannelImpl { if (blocking) { synchronized (stateLock) { - writerThread = 0; + writerThread = null; if (state == ST_CLOSING) { tryFinishClose(); } @@ -1714,7 +1714,7 @@ class DatagramChannelImpl */ private boolean tryClose() throws IOException { assert Thread.holdsLock(stateLock) && state == ST_CLOSING; - if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) { + if ((readerThread == null) && (writerThread == null) && !isRegistered()) { state = ST_CLOSED; try { // close socket 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 45f8cb2e588..df6677ab94d 100644 --- a/src/java.base/share/classes/sun/nio/ch/IOUtil.java +++ b/src/java.base/share/classes/sun/nio/ch/IOUtil.java @@ -587,9 +587,11 @@ public final class IOUtil { */ static native int drain1(int fd) throws IOException; - public static native void configureBlocking(FileDescriptor fd, - boolean blocking) - throws IOException; + static native void configureBlocking(int fd, boolean blocking) throws IOException; + + public static void configureBlocking(FileDescriptor fd, boolean blocking) throws IOException { + configureBlocking(fdVal(fd), blocking); + } public static native int fdVal(FileDescriptor fd); diff --git a/src/java.base/share/classes/sun/nio/ch/NativeDispatcher.java b/src/java.base/share/classes/sun/nio/ch/NativeDispatcher.java index 9b65310784a..63009b407ac 100644 --- a/src/java.base/share/classes/sun/nio/ch/NativeDispatcher.java +++ b/src/java.base/share/classes/sun/nio/ch/NativeDispatcher.java @@ -27,8 +27,6 @@ package sun.nio.ch; import java.io.FileDescriptor; import java.io.IOException; -import jdk.internal.access.JavaIOFileDescriptorAccess; -import jdk.internal.access.SharedSecrets; /** * Allows different platforms to call different native methods @@ -36,7 +34,6 @@ import jdk.internal.access.SharedSecrets; */ abstract class NativeDispatcher { - private static final JavaIOFileDescriptorAccess JIOFDA = SharedSecrets.getJavaIOFileDescriptorAccess(); abstract int read(FileDescriptor fd, long address, int len) throws IOException; @@ -78,12 +75,17 @@ abstract class NativeDispatcher { * if a platform thread is blocked on the file descriptor then the file descriptor is * dup'ed to a special fd and the thread signalled so that the syscall fails with EINTR. */ - final void preClose(FileDescriptor fd, long reader, long writer) throws IOException { - if (NativeThread.isVirtualThread(reader) || NativeThread.isVirtualThread(writer)) { - int fdVal = JIOFDA.get(fd); - Poller.stopPoll(fdVal); + final void preClose(FileDescriptor fd, Thread reader, Thread writer) throws IOException { + if (reader != null && reader.isVirtual()) { + NativeThread.signal(reader); // unparks virtual thread + reader = null; } - if (NativeThread.isNativeThread(reader) || NativeThread.isNativeThread(writer)) { + if (writer != null && writer.isVirtual()) { + NativeThread.signal(writer); // unparks virtual thread + writer = null; + } + // dup2 and signal platform threads + if (reader != null || writer != null) { implPreClose(fd, reader, writer); } } @@ -92,8 +94,7 @@ abstract class NativeDispatcher { * This method does nothing by default. On Unix systems the file descriptor is dup'ed * to a special fd and native threads signalled. */ - - void implPreClose(FileDescriptor fd, long reader, long writer) throws IOException { + void implPreClose(FileDescriptor fd, Thread reader, Thread writer) throws IOException { // Do nothing by default; this is only needed on Unix } diff --git a/src/java.base/share/classes/sun/nio/ch/NativeThreadSet.java b/src/java.base/share/classes/sun/nio/ch/NativeThreadSet.java index 079291572a8..c5423141789 100644 --- a/src/java.base/share/classes/sun/nio/ch/NativeThreadSet.java +++ b/src/java.base/share/classes/sun/nio/ch/NativeThreadSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -31,9 +31,9 @@ class NativeThreadSet { private static final int OTHER_THREAD_INDEX = -99; private final int initialCapacity; - private long[] threads; // array of thread handles, created lazily - private int used; // number of thread handles in threads array - private int otherThreads; // count of threads without a native thread handle + private Thread[] threads; // array of platform threads, created lazily + private int used; // number of elements in threads array + private int otherThreads; // additional threads that can't be signalled private boolean waitingToEmpty; NativeThreadSet(int n) { @@ -45,28 +45,28 @@ class NativeThreadSet { * it can efficiently be removed later. */ int add() { - long th = NativeThread.current(); synchronized (this) { - if (!NativeThread.isNativeThread(th)) { + final Thread t = NativeThread.threadToSignal(); + if (t == null || t.isVirtual()) { otherThreads++; return OTHER_THREAD_INDEX; } - // add native thread handle to array, creating or growing array if needed + // add platform threads to array, creating or growing array if needed int start = 0; if (threads == null) { - threads = new long[initialCapacity]; + threads = new Thread[initialCapacity]; } else if (used >= threads.length) { int on = threads.length; int nn = on * 2; - long[] nthreads = new long[nn]; + Thread[] nthreads = new Thread[nn]; System.arraycopy(threads, 0, nthreads, 0, on); threads = nthreads; start = on; } for (int i = start; i < threads.length; i++) { - if (threads[i] == 0) { - threads[i] = th; + if (threads[i] == null) { + threads[i] = t; used++; return i; } @@ -81,8 +81,7 @@ class NativeThreadSet { void remove(int i) { synchronized (this) { if (i >= 0) { - assert threads[i] == NativeThread.current(); - threads[i] = 0; + threads[i] = null; used--; } else if (i == OTHER_THREAD_INDEX) { otherThreads--; @@ -104,9 +103,9 @@ class NativeThreadSet { while (used > 0 || otherThreads > 0) { int u = used, i = 0; while (u > 0 && i < threads.length) { - long th = threads[i]; - if (th != 0) { - NativeThread.signal(th); + Thread t = threads[i]; + if (t != null) { + NativeThread.signal(t); u--; } i++; diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index 01f894be227..58b3bc7aaba 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -103,8 +103,8 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp private volatile boolean nonBlocking; // used by connect/read/write/accept, protected by stateLock - private long readerThread; - private long writerThread; + private Thread readerThread; + private Thread writerThread; // used when SO_REUSEADDR is emulated, protected by stateLock private boolean isReuseAddress; @@ -218,7 +218,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp private FileDescriptor beginRead() throws SocketException { synchronized (stateLock) { ensureOpenAndConnected(); - readerThread = NativeThread.current(); + readerThread = NativeThread.threadToSignal(); return fd; } } @@ -229,7 +229,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp */ private void endRead(boolean completed) throws SocketException { synchronized (stateLock) { - readerThread = 0; + readerThread = null; int state = this.state; if (state == ST_CLOSING) tryFinishClose(); @@ -370,7 +370,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp private FileDescriptor beginWrite() throws SocketException { synchronized (stateLock) { ensureOpenAndConnected(); - writerThread = NativeThread.current(); + writerThread = NativeThread.threadToSignal(); return fd; } } @@ -381,7 +381,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp */ private void endWrite(boolean completed) throws SocketException { synchronized (stateLock) { - writerThread = 0; + writerThread = null; int state = this.state; if (state == ST_CLOSING) tryFinishClose(); @@ -511,7 +511,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp this.address = address; this.port = port; - readerThread = NativeThread.current(); + readerThread = NativeThread.threadToSignal(); return fd; } } @@ -522,7 +522,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp */ private void endConnect(FileDescriptor fd, boolean completed) throws IOException { synchronized (stateLock) { - readerThread = 0; + readerThread = null; int state = this.state; if (state == ST_CLOSING) tryFinishClose(); @@ -666,7 +666,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp ensureOpen(); if (localport == 0) throw new SocketException("Not bound"); - readerThread = NativeThread.current(); + readerThread = NativeThread.threadToSignal(); return fd; } } @@ -678,7 +678,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp private void endAccept(boolean completed) throws SocketException { synchronized (stateLock) { int state = this.state; - readerThread = 0; + readerThread = null; if (state == ST_CLOSING) tryFinishClose(); if (!completed && state >= ST_CLOSING) @@ -844,7 +844,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp */ private boolean tryClose() throws IOException { assert Thread.holdsLock(stateLock) && state == ST_CLOSING; - if (readerThread == 0 && writerThread == 0) { + if (readerThread == null && writerThread == null) { try { cleaner.clean(); } catch (UncheckedIOException ioe) { @@ -1143,8 +1143,8 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp ensureOpenAndConnected(); if (!isInputClosed) { Net.shutdown(fd, Net.SHUT_RD); - if (NativeThread.isVirtualThread(readerThread)) { - Poller.stopPoll(fdVal(fd), Net.POLLIN); + if (readerThread != null && readerThread.isVirtual()) { + Poller.stopPoll(readerThread); } isInputClosed = true; } @@ -1157,8 +1157,8 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp ensureOpenAndConnected(); if (!isOutputClosed) { Net.shutdown(fd, Net.SHUT_WR); - if (NativeThread.isVirtualThread(writerThread)) { - Poller.stopPoll(fdVal(fd), Net.POLLOUT); + if (writerThread != null && writerThread.isVirtual()) { + Poller.stopPoll(writerThread); } isOutputClosed = true; } diff --git a/src/java.base/share/classes/sun/nio/ch/Poller.java b/src/java.base/share/classes/sun/nio/ch/Poller.java index 4a2cb4d8fdf..9360bcc8327 100644 --- a/src/java.base/share/classes/sun/nio/ch/Poller.java +++ b/src/java.base/share/classes/sun/nio/ch/Poller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -25,61 +25,107 @@ package sun.nio.ch; import java.io.IOException; +import java.io.UncheckedIOException; +import java.lang.ref.Reference; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.locks.LockSupport; import java.util.function.BooleanSupplier; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.misc.InnocuousThread; +import jdk.internal.misc.TerminatingThreadLocal; +import jdk.internal.vm.Continuation; +import jdk.internal.vm.ContinuationSupport; import jdk.internal.vm.annotation.Stable; /** - * Polls file descriptors. Virtual threads invoke the poll method to park - * until a given file descriptor is ready for I/O. + * I/O poller to allow virtual threads park until a file descriptor is ready for I/O. */ public abstract class Poller { - private static final Pollers POLLERS; - static { - try { - var pollers = new Pollers(); - pollers.start(); - POLLERS = pollers; - } catch (IOException ioe) { - throw new ExceptionInInitializerError(ioe); - } - } + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); - // the poller or sub-poller thread + // the poller group for the I/O pollers and poller threads + private static final PollerGroup POLLER_GROUP = createPollerGroup(); + + // the poller or sub-poller thread (used for observability only) private @Stable Thread owner; // maps file descriptors to parked Thread private final Map map = new ConcurrentHashMap<>(); + // shutdown (if supported by poller group) + private volatile boolean shutdown; + /** * Poller mode. */ enum Mode { /** - * ReadPoller and WritePoller are dedicated platform threads that block waiting - * for events and unpark virtual threads when file descriptors are ready for I/O. + * Read and write pollers are platform threads that block waiting for events and + * unpark virtual threads when file descriptors are ready for I/O. */ SYSTEM_THREADS, /** - * ReadPoller and WritePoller threads are virtual threads that poll for events, - * yielding between polls and unparking virtual threads when file descriptors are + * Read and write pollers are virtual threads that poll for events, yielding + * between polls and unparking virtual threads when file descriptors are * ready for I/O. If there are no events then the poller threads park until there * are I/O events to poll. This mode helps to integrate polling with virtual * thread scheduling. The approach is similar to the default scheme in "User-level * Threading: Have Your Cake and Eat It Too" by Karsten and Barghi 2020 * (https://dl.acm.org/doi/10.1145/3379483). */ - VTHREAD_POLLERS + VTHREAD_POLLERS, + + /** + * Read pollers are per-carrier virtual threads that poll for events, yielding + * between polls and unparking virtual threads when file descriptors are ready + * for I/O. If there are no events then the poller threads park until there + * are I/O events to poll. The write poller is a system-wide platform thread. + */ + POLLER_PER_CARRIER + } + + /** + * Create and return the PollerGroup. + */ + private static PollerGroup createPollerGroup() { + try { + PollerProvider provider; + if (System.getProperty("jdk.pollerMode") instanceof String s) { + Mode mode = switch (s) { + case "1" -> Mode.SYSTEM_THREADS; + case "2" -> Mode.VTHREAD_POLLERS; + case "3" -> Mode.POLLER_PER_CARRIER; + default -> { + throw new RuntimeException(s + " is not a valid polling mode"); + } + }; + provider = PollerProvider.createProvider(mode); + } else { + provider = PollerProvider.createProvider(); + } + + int readPollers = pollerCount("jdk.readPollers", provider.defaultReadPollers()); + int writePollers = pollerCount("jdk.writePollers", provider.defaultWritePollers()); + PollerGroup group = switch (provider.pollerMode()) { + case SYSTEM_THREADS -> new SystemThreadsPollerGroup(provider, readPollers, writePollers); + case VTHREAD_POLLERS -> new VThreadsPollerGroup(provider, readPollers, writePollers); + case POLLER_PER_CARRIER -> new PollerPerCarrierPollerGroup(provider, writePollers); + }; + group.start(); + return group; + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } } /** @@ -89,9 +135,34 @@ public abstract class Poller { } /** - * Returns the poller's file descriptor, used when the read and write poller threads - * are virtual threads. - * + * Closes the poller and release resources. This method can only be used to cleanup + * when creating a poller group fails. + */ + abstract void close() throws IOException; + + /** + * Sets the poller's thread owner. + */ + private void setOwner() { + owner = Thread.currentThread(); + } + + /** + * Returns true if this poller is marked for shutdown. + */ + boolean isShutdown() { + return shutdown; + } + + /** + * Marks this poller for shutdown. + */ + private void setShutdown() { + shutdown = true; + } + + /** + * Returns the poller's file descriptor to use when polling with the master poller. * @throws UnsupportedOperationException if not supported */ int fdVal() { @@ -99,16 +170,18 @@ public abstract class Poller { } /** - * Register the file descriptor. The registration is "one shot", meaning it should - * be polled at most once. + * Register the file descriptor with the I/O event management facility so that it is + * polled when the file descriptor is ready for I/O. The registration is "one shot", + * meaning it should be polled at most once. */ - abstract void implRegister(int fdVal) throws IOException; + abstract void implStartPoll(int fdVal) throws IOException; /** - * Deregister the file descriptor. + * Deregister a file descriptor from the I/O event management facility. This may be + * a no-op in some implementations when the file descriptor has already been polled. * @param polled true if the file descriptor has already been polled */ - abstract void implDeregister(int fdVal, boolean polled); + abstract void implStopPoll(int fdVal, boolean polled) throws IOException; /** * Poll for events. The {@link #polled(int)} method is invoked for each @@ -116,15 +189,26 @@ public abstract class Poller { * * @param timeout if positive then block for up to {@code timeout} milliseconds, * if zero then don't block, if -1 then block indefinitely - * @return the number of file descriptors polled + * @return >0 if file descriptors are polled, 0 if no file descriptor polled */ abstract int poll(int timeout) throws IOException; + /** + * Wakeup the poller thread if blocked in poll so it can shutdown. + * @throws UnsupportedOperationException if not supported + */ + void wakeupPoller() throws IOException { + throw new UnsupportedOperationException(); + } + /** * Callback by the poll method when a file descriptor is polled. */ final void polled(int fdVal) { - wakeup(fdVal); + Thread t = map.remove(fdVal); + if (t != null) { + LockSupport.unpark(t); + } } /** @@ -132,19 +216,10 @@ public abstract class Poller { * @param fdVal the file descriptor * @param event POLLIN or POLLOUT * @param nanos the waiting time or 0 to wait indefinitely - * @param supplier supplies a boolean to indicate if the enclosing object is open + * @param isOpen supplies a boolean to indicate if the enclosing object is open */ - static void poll(int fdVal, int event, long nanos, BooleanSupplier supplier) - throws IOException - { - assert nanos >= 0L; - if (event == Net.POLLIN) { - POLLERS.readPoller(fdVal).poll(fdVal, nanos, supplier); - } else if (event == Net.POLLOUT) { - POLLERS.writePoller(fdVal).poll(fdVal, nanos, supplier); - } else { - assert false; - } + public static void poll(int fdVal, int event, long nanos, BooleanSupplier isOpen) throws IOException { + POLLER_GROUP.poll(fdVal, event, nanos, isOpen); } /** @@ -152,45 +227,24 @@ public abstract class Poller { * @param fdVal the Selector's file descriptor * @param nanos the waiting time or 0 to wait indefinitely */ - static void pollSelector(int fdVal, long nanos) throws IOException { - assert nanos >= 0L; - Poller poller = POLLERS.masterPoller(); - if (poller == null) { - poller = POLLERS.readPoller(fdVal); - } - poller.poll(fdVal, nanos, () -> true); + public static void pollSelector(int fdVal, long nanos) throws IOException { + POLLER_GROUP.pollSelector(fdVal, nanos); } /** - * If there is a thread polling the given file descriptor for the given event then - * the thread is unparked. + * Unpark the given thread so that it stops polling. */ - static void stopPoll(int fdVal, int event) { - if (event == Net.POLLIN) { - POLLERS.readPoller(fdVal).wakeup(fdVal); - } else if (event == Net.POLLOUT) { - POLLERS.writePoller(fdVal).wakeup(fdVal); - } else { - throw new IllegalArgumentException(); - } - } - - /** - * If there are any threads polling the given file descriptor then they are unparked. - */ - static void stopPoll(int fdVal) { - stopPoll(fdVal, Net.POLLIN); - stopPoll(fdVal, Net.POLLOUT); + public static void stopPoll(Thread thread) { + LockSupport.unpark(thread); } /** * Parks the current thread until a file descriptor is ready. */ - private void poll(int fdVal, long nanos, BooleanSupplier supplier) throws IOException { - register(fdVal); + private void poll(int fdVal, long nanos, BooleanSupplier isOpen) throws IOException { + startPoll(fdVal); try { - boolean isOpen = supplier.getAsBoolean(); - if (isOpen) { + if (isOpen.getAsBoolean() && !isShutdown()) { if (nanos > 0) { LockSupport.parkNanos(nanos); } else { @@ -198,42 +252,38 @@ public abstract class Poller { } } } finally { - deregister(fdVal); + stopPoll(fdVal); } } /** - * Registers the file descriptor to be polled at most once when the file descriptor - * is ready for I/O. + * Register a file descriptor with the I/O event management facility so that it is + * polled when the file descriptor is ready for I/O. */ - private void register(int fdVal) throws IOException { + private void startPoll(int fdVal) throws IOException { Thread previous = map.put(fdVal, Thread.currentThread()); assert previous == null; try { - implRegister(fdVal); + implStartPoll(fdVal); } catch (Throwable t) { map.remove(fdVal); throw t; + } finally { + Reference.reachabilityFence(this); } } /** - * Deregister the file descriptor so that the file descriptor is not polled. + * Deregister a file descriptor from the I/O event management facility. */ - private void deregister(int fdVal) { + private void stopPoll(int fdVal) throws IOException { Thread previous = map.remove(fdVal); boolean polled = (previous == null); assert polled || previous == Thread.currentThread(); - implDeregister(fdVal, polled); - } - - /** - * Unparks any thread that is polling the given file descriptor. - */ - private void wakeup(int fdVal) { - Thread t = map.remove(fdVal); - if (t != null) { - LockSupport.unpark(t); + try { + implStopPoll(fdVal, polled); + } finally { + Reference.reachabilityFence(this); } } @@ -242,9 +292,9 @@ public abstract class Poller { * descriptor that is polled. */ private void pollerLoop() { - owner = Thread.currentThread(); + setOwner(); try { - for (;;) { + while (!isShutdown()) { poll(-1); } } catch (Exception e) { @@ -263,10 +313,10 @@ public abstract class Poller { */ private void subPollerLoop(Poller masterPoller) { assert Thread.currentThread().isVirtual(); - owner = Thread.currentThread(); + setOwner(); try { int polled = 0; - for (;;) { + while (!isShutdown()) { if (polled == 0) { masterPoller.poll(fdVal(), 0, () -> true); // park } else { @@ -280,194 +330,463 @@ public abstract class Poller { } /** - * Returns the number I/O operations currently registered with this poller. + * Unparks all threads waiting on a file descriptor registered with this poller. */ - public int registered() { - return map.size(); + private void wakeupAll() { + map.values().forEach(LockSupport::unpark); } @Override public String toString() { return String.format("%s [registered = %d, owner = %s]", - Objects.toIdentityString(this), registered(), owner); + Objects.toIdentityString(this), map.size(), owner); } /** - * The Pollers used for read and write events. + * A group of poller threads that support virtual threads polling file descriptors. */ - private static class Pollers { + private static abstract class PollerGroup { private final PollerProvider provider; - private final Poller.Mode pollerMode; - private final Poller masterPoller; - private final Poller[] readPollers; - private final Poller[] writePollers; - - // used by start method to executor is kept alive - private Executor executor; - - /** - * Creates the Poller instances based on configuration. - */ - Pollers() throws IOException { - PollerProvider provider = PollerProvider.provider(); - Poller.Mode mode; - String s = System.getProperty("jdk.pollerMode"); - if (s != null) { - if (s.equalsIgnoreCase(Mode.SYSTEM_THREADS.name()) || s.equals("1")) { - mode = Mode.SYSTEM_THREADS; - } else if (s.equalsIgnoreCase(Mode.VTHREAD_POLLERS.name()) || s.equals("2")) { - mode = Mode.VTHREAD_POLLERS; - } else { - throw new RuntimeException("Can't parse '" + s + "' as polling mode"); - } - } else { - mode = provider.defaultPollerMode(); - } - - // vthread poller mode needs a master poller - Poller masterPoller = (mode == Mode.VTHREAD_POLLERS) - ? provider.readPoller(false) - : null; - - // read pollers (or sub-pollers) - int readPollerCount = pollerCount("jdk.readPollers", provider.defaultReadPollers(mode)); - Poller[] readPollers = new Poller[readPollerCount]; - for (int i = 0; i < readPollerCount; i++) { - readPollers[i] = provider.readPoller(mode == Mode.VTHREAD_POLLERS); - } - - // write pollers (or sub-pollers) - int writePollerCount = pollerCount("jdk.writePollers", provider.defaultWritePollers(mode)); - Poller[] writePollers = new Poller[writePollerCount]; - for (int i = 0; i < writePollerCount; i++) { - writePollers[i] = provider.writePoller(mode == Mode.VTHREAD_POLLERS); - } + PollerGroup(PollerProvider provider) { this.provider = provider; - this.pollerMode = mode; - this.masterPoller = masterPoller; - this.readPollers = readPollers; - this.writePollers = writePollers; + } + + final PollerProvider provider() { + return provider; } /** - * Starts the Poller threads. + * Starts the poller group and any system-wide poller threads. */ - void start() { - if (pollerMode == Mode.VTHREAD_POLLERS) { - startPlatformThread("MasterPoller", masterPoller::pollerLoop); - ThreadFactory factory = Thread.ofVirtual() - .inheritInheritableThreadLocals(false) - .name("SubPoller-", 0) - .uncaughtExceptionHandler((t, e) -> e.printStackTrace()) - .factory(); - executor = Executors.newThreadPerTaskExecutor(factory); - Arrays.stream(readPollers).forEach(p -> { - executor.execute(() -> p.subPollerLoop(masterPoller)); - }); - Arrays.stream(writePollers).forEach(p -> { - executor.execute(() -> p.subPollerLoop(masterPoller)); - }); - } else { - Arrays.stream(readPollers).forEach(p -> { - startPlatformThread("Read-Poller", p::pollerLoop); - }); - Arrays.stream(writePollers).forEach(p -> { - startPlatformThread("Write-Poller", p::pollerLoop); - }); - } - } + abstract void start(); /** - * Returns the master poller, or null if there is no master poller. + * Parks the current thread until a file descriptor is ready for the given op. */ - Poller masterPoller() { - return masterPoller; - } + abstract void poll(int fdVal, int event, long nanos, BooleanSupplier isOpen) throws IOException; /** - * Returns the read poller for the given file descriptor. + * Parks the current thread until a Selector's file descriptor is ready. */ - Poller readPoller(int fdVal) { - int index = provider.fdValToIndex(fdVal, readPollers.length); - return readPollers[index]; - } - - /** - * Returns the write poller for the given file descriptor. - */ - Poller writePoller(int fdVal) { - int index = provider.fdValToIndex(fdVal, writePollers.length); - return writePollers[index]; - } - - /** - * Return the list of read pollers. - */ - List readPollers() { - return List.of(readPollers); - } - - /** - * Return the list of write pollers. - */ - List writePollers() { - return List.of(writePollers); - } - - - /** - * Reads the given property name to get the poller count. If the property is - * set then the value must be a power of 2. Returns 1 if the property is not - * set. - * @throws IllegalArgumentException if the property is set to a value that - * is not a power of 2. - */ - private static int pollerCount(String propName, int defaultCount) { - String s = System.getProperty(propName); - int count = (s != null) ? Integer.parseInt(s) : defaultCount; - - // check power of 2 - if (count != Integer.highestOneBit(count)) { - String msg = propName + " is set to a value that is not a power of 2"; - throw new IllegalArgumentException(msg); - } - return count; + void pollSelector(int fdVal, long nanos) throws IOException { + poll(fdVal, Net.POLLIN, nanos, () -> true); } /** * Starts a platform thread to run the given task. */ - private void startPlatformThread(String name, Runnable task) { - try { - Thread thread = InnocuousThread.newSystemThread(name, task); - thread.setDaemon(true); - thread.setUncaughtExceptionHandler((t, e) -> e.printStackTrace()); - thread.start(); - } catch (Exception e) { - throw new InternalError(e); + protected final void startPlatformThread(String name, Runnable task) { + Thread thread = InnocuousThread.newSystemThread(name, task); + thread.setDaemon(true); + thread.setUncaughtExceptionHandler((t, e) -> e.printStackTrace()); + thread.start(); + } + + /** + * Return the master poller, or null if no master poller. + */ + abstract Poller masterPoller(); + + /** + * Return the read pollers. + */ + abstract List readPollers(); + + /** + * Return the write pollers. + */ + abstract List writePollers(); + + /** + * Close the given pollers. + */ + static void closeAll(Poller... pollers) { + for (Poller poller : pollers) { + if (poller != null) { + try { + poller.close(); + } catch (IOException _) { } + } } } } + /** + * SYSTEM_THREADS poller group. The read and write pollers are system-wide platform threads. + */ + private static class SystemThreadsPollerGroup extends PollerGroup { + // system-wide read and write pollers + private final Poller[] readPollers; + private final Poller[] writePollers; + + SystemThreadsPollerGroup(PollerProvider provider, + int readPollerCount, + int writePollerCount) throws IOException { + super(provider); + Poller[] readPollers = new Poller[readPollerCount]; + Poller[] writePollers = new Poller[writePollerCount]; + try { + for (int i = 0; i < readPollerCount; i++) { + readPollers[i] = provider.readPoller(false); + } + for (int i = 0; i < writePollerCount; i++) { + writePollers[i] = provider.writePoller(false); + } + } catch (Throwable e) { + closeAll(readPollers); + closeAll(writePollers); + throw e; + } + + this.readPollers = readPollers; + this.writePollers = writePollers; + } + + @Override + void start() { + Arrays.stream(readPollers).forEach(p -> { + startPlatformThread("Read-Poller", p::pollerLoop); + }); + Arrays.stream(writePollers).forEach(p -> { + startPlatformThread("Write-Poller", p::pollerLoop); + }); + } + + private Poller readPoller(int fdVal) { + int index = provider().fdValToIndex(fdVal, readPollers.length); + return readPollers[index]; + } + + private Poller writePoller(int fdVal) { + int index = provider().fdValToIndex(fdVal, writePollers.length); + return writePollers[index]; + } + + @Override + void poll(int fdVal, int event, long nanos, BooleanSupplier isOpen) throws IOException { + Poller poller = (event == Net.POLLIN) + ? readPoller(fdVal) + : writePoller(fdVal); + poller.poll(fdVal, nanos, isOpen); + } + + @Override + Poller masterPoller() { + return null; + } + + @Override + List readPollers() { + return List.of(readPollers); + } + + @Override + List writePollers() { + return List.of(writePollers); + } + } + + /** + * VTHREAD_POLLERS poller group. The read and write pollers are virtual threads. + * When read and write pollers need to block then they register with a system-wide + * "master poller" that runs in a dedicated platform thread. + */ + private static class VThreadsPollerGroup extends PollerGroup { + private final Poller masterPoller; + private final Poller[] readPollers; + private final Poller[] writePollers; + + // keep virtual thread pollers alive + private final Executor executor; + + VThreadsPollerGroup(PollerProvider provider, + int readPollerCount, + int writePollerCount) throws IOException { + super(provider); + Poller masterPoller = provider.readPoller(false); + Poller[] readPollers = new Poller[readPollerCount]; + Poller[] writePollers = new Poller[writePollerCount]; + + try { + for (int i = 0; i < readPollerCount; i++) { + readPollers[i] = provider.readPoller(true); + } + for (int i = 0; i < writePollerCount; i++) { + writePollers[i] = provider.writePoller(true); + } + } catch (Throwable e) { + masterPoller.close(); + closeAll(readPollers); + closeAll(writePollers); + throw e; + } + + this.masterPoller = masterPoller; + this.readPollers = readPollers; + this.writePollers = writePollers; + + ThreadFactory factory = Thread.ofVirtual() + .inheritInheritableThreadLocals(false) + .name("SubPoller-", 0) + .uncaughtExceptionHandler((_, e) -> e.printStackTrace()) + .factory(); + this.executor = Executors.newThreadPerTaskExecutor(factory); + } + + @Override + void start() { + startPlatformThread("Master-Poller", masterPoller::pollerLoop); + Arrays.stream(readPollers).forEach(p -> { + executor.execute(() -> p.subPollerLoop(masterPoller)); + }); + Arrays.stream(writePollers).forEach(p -> { + executor.execute(() -> p.subPollerLoop(masterPoller)); + }); + } + + private Poller readPoller(int fdVal) { + int index = provider().fdValToIndex(fdVal, readPollers.length); + return readPollers[index]; + } + + private Poller writePoller(int fdVal) { + int index = provider().fdValToIndex(fdVal, writePollers.length); + return writePollers[index]; + } + + @Override + void poll(int fdVal, int event, long nanos, BooleanSupplier isOpen) throws IOException { + Poller poller = (event == Net.POLLIN) + ? readPoller(fdVal) + : writePoller(fdVal); + poller.poll(fdVal, nanos, isOpen); + } + + @Override + void pollSelector(int fdVal, long nanos) throws IOException { + masterPoller.poll(fdVal, nanos, () -> true); + } + + @Override + Poller masterPoller() { + return masterPoller; + } + + @Override + List readPollers() { + return List.of(readPollers); + } + + @Override + List writePollers() { + return List.of(writePollers); + } + } + + /** + * POLLER_PER_CARRIER poller group. The read poller is a per-carrier virtual thread. + * When a virtual thread polls a file descriptor for POLLIN, then it will use (almost + * always, not guaranteed) the read poller for its carrier. When a read poller needs + * to block then it registers with a system-wide "master poller" that runs in a + * dedicated platform thread. The read poller terminates if the carrier terminates. + * The write pollers are system-wide platform threads (usually one). + */ + private static class PollerPerCarrierPollerGroup extends PollerGroup { + private record CarrierPoller(PollerPerCarrierPollerGroup group, Poller readPoller) { } + private static final TerminatingThreadLocal CARRIER_POLLER = + new TerminatingThreadLocal<>() { + @Override + protected void threadTerminated(CarrierPoller carrierPoller) { + Poller readPoller = carrierPoller.readPoller(); + carrierPoller.group().carrierTerminated(readPoller); + } + }; + + private final Poller masterPoller; + private final Set readPollers; + private final Poller[] writePollers; + + /** + * Create a PollerPerCarrierPollerGroup with the given number of write pollers. + */ + PollerPerCarrierPollerGroup(PollerProvider provider, + int writePollerCount) throws IOException { + super(provider); + Poller masterPoller = provider.readPoller(false); + Poller[] writePollers = new Poller[writePollerCount]; + try { + for (int i = 0; i < writePollerCount; i++) { + writePollers[i] = provider.writePoller(false); + } + } catch (Throwable e) { + masterPoller.close(); + closeAll(writePollers); + throw e; + } + this.masterPoller = masterPoller; + this.readPollers = ConcurrentHashMap.newKeySet();; + this.writePollers = writePollers; + } + + @Override + void start() { + startPlatformThread("Master-Poller", masterPoller::pollerLoop); + Arrays.stream(writePollers).forEach(p -> { + startPlatformThread("Write-Poller", p::pollerLoop); + }); + } + + private Poller writePoller(int fdVal) { + int index = provider().fdValToIndex(fdVal, writePollers.length); + return writePollers[index]; + } + + /** + * Starts a read sub-poller in a virtual thread. + */ + private Poller startReadPoller() throws IOException { + assert Thread.currentThread().isVirtual() && ContinuationSupport.isSupported(); + + // create read sub-poller + Poller readPoller = provider().readPoller(true); + readPollers.add(readPoller); + + // start virtual thread to execute sub-polling loop + Thread carrier = JLA.currentCarrierThread(); + Thread.ofVirtual() + .inheritInheritableThreadLocals(false) + .name(carrier.getName() + "-Read-Poller") + .uncaughtExceptionHandler((_, e) -> e.printStackTrace()) + .start(() -> subPollerLoop(readPoller)); + return readPoller; + } + + /** + * Returns the read poller for the current carrier, starting it if required. + */ + private Poller readPoller() throws IOException { + assert Thread.currentThread().isVirtual() && ContinuationSupport.isSupported(); + Continuation.pin(); + try { + CarrierPoller carrierPoller = CARRIER_POLLER.get(); + if (carrierPoller != null) { + return carrierPoller.readPoller(); + } else { + // first poll on this carrier will start poller + Poller readPoller = startReadPoller(); + CARRIER_POLLER.set(new CarrierPoller(this, readPoller)); + return readPoller; + } + } finally { + Continuation.unpin(); + } + } + + @Override + void poll(int fdVal, int event, long nanos, BooleanSupplier isOpen) throws IOException { + // for POLLIN, get the read poller for this carrier + if (event == Net.POLLIN + && Thread.currentThread().isVirtual() + && ContinuationSupport.isSupported()) { + readPoller().poll(fdVal, nanos, isOpen); + return; + } + + // -XX:-VMContinuations or POLLIN from platform thread does master poller + if (event == Net.POLLIN) { + masterPoller.poll(fdVal, nanos, isOpen); + } else { + writePoller(fdVal).poll(fdVal, nanos, isOpen); + } + } + + @Override + void pollSelector(int fdVal, long nanos) throws IOException { + masterPoller.poll(fdVal, nanos, () -> true); + } + + /** + * Sub-poller polling loop. + */ + private void subPollerLoop(Poller readPoller) { + try { + readPoller.subPollerLoop(masterPoller); + } finally { + // wakeup all threads waiting on file descriptors registered with the + // read poller, these I/O operation will migrate to another carrier. + readPoller.wakeupAll(); + + // remove from serviceability view + readPollers.remove(readPoller); + } + } + + /** + * Invoked by the carrier thread before it terminates. + */ + private void carrierTerminated(Poller readPoller) { + readPoller.setShutdown(); + try { + readPoller.wakeupPoller(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + @Override + Poller masterPoller() { + return masterPoller; + } + + @Override + List readPollers() { + return readPollers.stream().toList(); + } + + @Override + List writePollers() { + return List.of(writePollers); + } + } + + /** + * Reads the given property name to get the poller count. If the property is + * set then the value must be a power of 2. Returns 1 if the property is not + * set. + * @throws IllegalArgumentException if the property is set to a value that + * is not a power of 2. + */ + private static int pollerCount(String propName, int defaultCount) { + String s = System.getProperty(propName); + int count = (s != null) ? Integer.parseInt(s) : defaultCount; + + // check power of 2 + if (count != Integer.highestOneBit(count)) { + String msg = propName + " is set to a value that is not a power of 2"; + throw new IllegalArgumentException(msg); + } + return count; + } + /** * Return the master poller or null if there is no master poller. */ public static Poller masterPoller() { - return POLLERS.masterPoller(); + return POLLER_GROUP.masterPoller(); } /** * Return the list of read pollers. */ public static List readPollers() { - return POLLERS.readPollers(); + return POLLER_GROUP.readPollers(); } /** * Return the list of write pollers. */ public static List writePollers() { - return POLLERS.writePollers(); + return POLLER_GROUP.writePollers(); } } diff --git a/src/java.base/share/classes/sun/nio/ch/PollerProvider.java b/src/java.base/share/classes/sun/nio/ch/PollerProvider.java index b10ec309265..7d19b72d2fc 100644 --- a/src/java.base/share/classes/sun/nio/ch/PollerProvider.java +++ b/src/java.base/share/classes/sun/nio/ch/PollerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 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 @@ -30,38 +30,43 @@ import java.io.IOException; * Provider class for Poller implementations. */ abstract class PollerProvider { - private static final PollerProvider INSTANCE = new DefaultPollerProvider(); + private final Poller.Mode mode; - PollerProvider() { } + PollerProvider(Poller.Mode mode) { + this.mode = mode; + } - /** - * Returns the system-wide PollerProvider. - */ - static PollerProvider provider() { - return INSTANCE; + final Poller.Mode pollerMode() { + return mode; } /** - * Returns the default poller mode. - * @implSpec The default implementation uses system threads. + * Creates a PollerProvider that uses its preferred/default poller mode. */ - Poller.Mode defaultPollerMode() { - return Poller.Mode.SYSTEM_THREADS; + static PollerProvider createProvider() { + return new DefaultPollerProvider(); } /** - * Default number of read pollers for the given mode. The count must be a power of 2. + * Creates a PollerProvider that uses the given poller mode. + */ + static PollerProvider createProvider(Poller.Mode mode) { + return new DefaultPollerProvider(mode); + } + + /** + * Default number of read pollers. The count must be a power of 2. * @implSpec The default implementation returns 1. */ - int defaultReadPollers(Poller.Mode mode) { + int defaultReadPollers() { return 1; } /** - * Default number of write pollers for the given mode. The count must be a power of 2. + * Default number of write pollers. The count must be a power of 2. * @implSpec The default implementation returns 1. */ - int defaultWritePollers(Poller.Mode mode) { + int defaultWritePollers() { return 1; } @@ -74,13 +79,13 @@ abstract class PollerProvider { } /** - * Creates a Poller for read ops. + * Creates a Poller for POLLIN polling. * @param subPoller true to create a sub-poller */ abstract Poller readPoller(boolean subPoller) throws IOException; /** - * Creates a Poller for write ops. + * Creates a Poller for POLLOUT polling. * @param subPoller true to create a sub-poller */ abstract Poller writePoller(boolean subPoller) throws IOException; diff --git a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java index e75110a76ad..98f79e6671b 100644 --- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java @@ -90,8 +90,8 @@ class ServerSocketChannelImpl private static final int ST_CLOSED = 2; private int state; - // ID of native thread currently blocked in this channel, for signalling - private long thread; + // Thread currently blocked in this channel, for signalling + private Thread thread; // Binding private SocketAddress localAddress; // null => unbound @@ -349,7 +349,7 @@ class ServerSocketChannelImpl if (localAddress == null) throw new NotYetBoundException(); if (blocking) - thread = NativeThread.current(); + thread = NativeThread.threadToSignal(); } } @@ -364,7 +364,7 @@ class ServerSocketChannelImpl { if (blocking) { synchronized (stateLock) { - thread = 0; + thread = null; if (state == ST_CLOSING) { tryFinishClose(); } @@ -551,7 +551,7 @@ class ServerSocketChannelImpl */ private boolean tryClose() throws IOException { assert Thread.holdsLock(stateLock) && state == ST_CLOSING; - if ((thread == 0) && !isRegistered()) { + if ((thread == null) && !isRegistered()) { state = ST_CLOSED; nd.close(fd); return true; @@ -583,7 +583,7 @@ class ServerSocketChannelImpl assert state < ST_CLOSING; state = ST_CLOSING; if (!tryClose()) { - nd.preClose(fd, thread, 0); + nd.preClose(fd, thread, null); } } } diff --git a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java index 868ed3b64bc..010b1832190 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -113,9 +113,9 @@ class SocketChannelImpl private static final int ST_CLOSED = 4; private volatile int state; // need stateLock to change - // IDs of native threads doing reads and writes, for signalling - private long readerThread; - private long writerThread; + // Threads doing reads and writes, for signalling + private Thread readerThread; + private Thread writerThread; // Binding private SocketAddress localAddress; @@ -368,7 +368,7 @@ class SocketChannelImpl synchronized (stateLock) { ensureOpen(); // record thread so it can be signalled if needed - readerThread = NativeThread.current(); + readerThread = NativeThread.threadToSignal(); } } } @@ -384,7 +384,7 @@ class SocketChannelImpl { if (blocking) { synchronized (stateLock) { - readerThread = 0; + readerThread = null; if (state == ST_CLOSING) { tryFinishClose(); } @@ -522,7 +522,7 @@ class SocketChannelImpl if (isOutputClosed) throw new ClosedChannelException(); // record thread so it can be signalled if needed - writerThread = NativeThread.current(); + writerThread = NativeThread.threadToSignal(); } } } @@ -538,7 +538,7 @@ class SocketChannelImpl { if (blocking) { synchronized (stateLock) { - writerThread = 0; + writerThread = null; if (state == ST_CLOSING) { tryFinishClose(); } @@ -673,7 +673,7 @@ class SocketChannelImpl ensureOpenAndConnected(); if (isOutputClosed) throw new ClosedChannelException(); - writerThread = NativeThread.current(); + writerThread = NativeThread.threadToSignal(); completed = true; } } finally { @@ -689,7 +689,7 @@ class SocketChannelImpl */ void afterTransferTo(boolean completed) throws AsynchronousCloseException { synchronized (stateLock) { - writerThread = 0; + writerThread = null; if (state == ST_CLOSING) { tryFinishClose(); } @@ -874,7 +874,7 @@ class SocketChannelImpl if (blocking) { // record thread so it can be signalled if needed - readerThread = NativeThread.current(); + readerThread = NativeThread.threadToSignal(); } } } @@ -993,7 +993,7 @@ class SocketChannelImpl throw new NoConnectionPendingException(); if (blocking) { // record thread so it can be signalled if needed - readerThread = NativeThread.current(); + readerThread = NativeThread.threadToSignal(); } } } @@ -1072,7 +1072,7 @@ class SocketChannelImpl */ private boolean tryClose() throws IOException { assert Thread.holdsLock(stateLock) && state == ST_CLOSING; - if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) { + if ((readerThread == null) && (writerThread == null) && !isRegistered()) { state = ST_CLOSED; nd.close(fd); return true; @@ -1215,11 +1215,8 @@ class SocketChannelImpl throw new NotYetConnectedException(); if (!isInputClosed) { Net.shutdown(fd, Net.SHUT_RD); - long reader = readerThread; - if (NativeThread.isVirtualThread(reader)) { - Poller.stopPoll(fdVal, Net.POLLIN); - } else if (NativeThread.isNativeThread(reader)) { - NativeThread.signal(reader); + if (readerThread != null && readerThread.isVirtual()) { + Poller.stopPoll(readerThread); } isInputClosed = true; } @@ -1235,11 +1232,8 @@ class SocketChannelImpl throw new NotYetConnectedException(); if (!isOutputClosed) { Net.shutdown(fd, Net.SHUT_WR); - long writer = writerThread; - if (NativeThread.isVirtualThread(writer)) { - Poller.stopPoll(fdVal, Net.POLLOUT); - } else if (NativeThread.isNativeThread(writer)) { - NativeThread.signal(writer); + if (writerThread != null && writerThread.isVirtual()) { + Poller.stopPoll(writerThread); } isOutputClosed = true; } diff --git a/src/java.base/unix/classes/sun/nio/ch/NativeThread.java b/src/java.base/unix/classes/sun/nio/ch/NativeThread.java index 8d0bcea48d9..75cf36d6d52 100644 --- a/src/java.base/unix/classes/sun/nio/ch/NativeThread.java +++ b/src/java.base/unix/classes/sun/nio/ch/NativeThread.java @@ -25,6 +25,9 @@ package sun.nio.ch; +import java.util.concurrent.locks.LockSupport; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; // Signalling operations on native threads // @@ -37,45 +40,38 @@ package sun.nio.ch; // always returns -1 and the signal(long) method has no effect. public class NativeThread { - private static final long VIRTUAL_THREAD_ID = -1L; + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + + private NativeThread() { } /** - * Returns the id of the current native thread if the platform can signal - * native threads, 0 if the platform can not signal native threads, or - * -1L if the current thread is a virtual thread. - */ - public static long current() { - if (Thread.currentThread().isVirtual()) { - return VIRTUAL_THREAD_ID; - } else { - return current0(); - } - } - - /** - * Signals the given native thread. + * Returns the Thread to signal the current thread. * - * @throws IllegalArgumentException if tid is not a token to a native thread + * The first use of this method on a platform thread will capture the thread's + * native thread ID. */ - public static void signal(long tid) { - if (tid == 0 || tid == VIRTUAL_THREAD_ID) - throw new IllegalArgumentException(); - signal0(tid); + public static Thread threadToSignal() { + Thread t = Thread.currentThread(); + if (!t.isVirtual() && JLA.nativeThreadID(t) == 0) { + JLA.setThreadNativeID(current0()); + } + return t; } /** - * Returns true the tid is the id of a native thread. + * Signals the given thread. For a platform thread it sends a signal to the thread. + * For a virtual thread it just unparks it. + * @throws IllegalStateException if the thread is a platform thread that hasn't set its native ID */ - static boolean isNativeThread(long tid) { - return (tid != 0 && tid != VIRTUAL_THREAD_ID); - } - - /** - * Returns true if tid is -1L. - * @see #current() - */ - static boolean isVirtualThread(long tid) { - return (tid == VIRTUAL_THREAD_ID); + public static void signal(Thread thread) { + if (thread.isVirtual()) { + LockSupport.unpark(thread); + } else { + long id = JLA.nativeThreadID(thread); + if (id == 0) + throw new IllegalStateException("Native thread ID not set"); + signal0(id); + } } /** diff --git a/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java b/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java index b073c287bfb..09d280b370c 100644 --- a/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java +++ b/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java @@ -63,8 +63,8 @@ class SinkChannelImpl private static final int ST_CLOSED = 2; private int state; - // ID of native thread doing write, for signalling - private long thread; + // Thread doing write, for signalling + private Thread writerThread; // True if the channel's socket has been forced into non-blocking mode // by a virtual thread. It cannot be reset. When the channel is in @@ -120,7 +120,7 @@ class SinkChannelImpl */ private boolean tryClose() throws IOException { assert Thread.holdsLock(stateLock) && state == ST_CLOSING; - if (thread == 0 && !isRegistered()) { + if (writerThread == null && !isRegistered()) { state = ST_CLOSED; nd.close(fd); return true; @@ -152,7 +152,7 @@ class SinkChannelImpl assert state < ST_CLOSING; state = ST_CLOSING; if (!tryClose()) { - nd.preClose(fd, thread, 0); + nd.preClose(fd, null, writerThread); } } } @@ -270,7 +270,7 @@ class SinkChannelImpl synchronized (stateLock) { ensureOpen(); if (blocking) - thread = NativeThread.current(); + writerThread = NativeThread.threadToSignal(); } } @@ -285,7 +285,7 @@ class SinkChannelImpl { if (blocking) { synchronized (stateLock) { - thread = 0; + writerThread = null; if (state == ST_CLOSING) { tryFinishClose(); } diff --git a/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java b/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java index 571d7f483d2..52188c12b17 100644 --- a/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java +++ b/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java @@ -63,8 +63,8 @@ class SourceChannelImpl private static final int ST_CLOSED = 2; private int state; - // ID of native thread doing read, for signalling - private long thread; + // Thread doing read, for signalling + private Thread readerThread; // True if the channel's socket has been forced into non-blocking mode // by a virtual thread. It cannot be reset. When the channel is in @@ -120,7 +120,7 @@ class SourceChannelImpl */ private boolean tryClose() throws IOException { assert Thread.holdsLock(stateLock) && state == ST_CLOSING; - if (thread == 0 && !isRegistered()) { + if (readerThread == null && !isRegistered()) { state = ST_CLOSED; nd.close(fd); return true; @@ -152,7 +152,7 @@ class SourceChannelImpl assert state < ST_CLOSING; state = ST_CLOSING; if (!tryClose()) { - nd.preClose(fd, thread, 0); + nd.preClose(fd, readerThread, null); } } } @@ -269,8 +269,9 @@ class SourceChannelImpl } synchronized (stateLock) { ensureOpen(); - if (blocking) - thread = NativeThread.current(); + if (blocking) { + readerThread = NativeThread.threadToSignal(); + } } } @@ -285,7 +286,7 @@ class SourceChannelImpl { if (blocking) { synchronized (stateLock) { - thread = 0; + readerThread = null; if (state == ST_CLOSING) { tryFinishClose(); } diff --git a/src/java.base/unix/classes/sun/nio/ch/UnixDispatcher.java b/src/java.base/unix/classes/sun/nio/ch/UnixDispatcher.java index 4cdd0c400ec..3656b172822 100644 --- a/src/java.base/unix/classes/sun/nio/ch/UnixDispatcher.java +++ b/src/java.base/unix/classes/sun/nio/ch/UnixDispatcher.java @@ -36,15 +36,17 @@ abstract class UnixDispatcher extends NativeDispatcher { close0(fd); } - private void signalThreads(long reader, long writer) { - if (NativeThread.isNativeThread(reader)) + private void signalThreads(Thread reader, Thread writer) { + if (reader != null) { NativeThread.signal(reader); - if (NativeThread.isNativeThread(writer)) + } + if (writer != null) { NativeThread.signal(writer); + } } @Override - void implPreClose(FileDescriptor fd, long reader, long writer) throws IOException { + void implPreClose(FileDescriptor fd, Thread reader, Thread writer) throws IOException { if (SUPPORTS_PENDING_SIGNALS) { signalThreads(reader, writer); } diff --git a/src/java.base/unix/native/libnio/ch/IOUtil.c b/src/java.base/unix/native/libnio/ch/IOUtil.c index dfa99658fa6..3a7693b2ee0 100644 --- a/src/java.base/unix/native/libnio/ch/IOUtil.c +++ b/src/java.base/unix/native/libnio/ch/IOUtil.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -77,10 +77,9 @@ configureBlocking(int fd, jboolean blocking) } JNIEXPORT void JNICALL -Java_sun_nio_ch_IOUtil_configureBlocking(JNIEnv *env, jclass clazz, - jobject fdo, jboolean blocking) +Java_sun_nio_ch_IOUtil_configureBlocking(JNIEnv *env, jclass clazz, jint fd, jboolean blocking) { - if (configureBlocking(fdval(env, fdo), blocking) < 0) + if (configureBlocking(fd, blocking) < 0) JNU_ThrowIOExceptionWithLastError(env, "Configure blocking failed"); } diff --git a/src/java.base/windows/classes/sun/nio/ch/DefaultPollerProvider.java b/src/java.base/windows/classes/sun/nio/ch/DefaultPollerProvider.java index abd2f34a229..d1af62fbd73 100644 --- a/src/java.base/windows/classes/sun/nio/ch/DefaultPollerProvider.java +++ b/src/java.base/windows/classes/sun/nio/ch/DefaultPollerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -30,11 +30,19 @@ import java.io.IOException; * Default PollerProvider for Windows based on wepoll. */ class DefaultPollerProvider extends PollerProvider { - DefaultPollerProvider() { } + DefaultPollerProvider(Poller.Mode mode) { + if (mode != Poller.Mode.SYSTEM_THREADS) { + throw new UnsupportedOperationException(); + } + super(mode); + } + + DefaultPollerProvider() { + this(Poller.Mode.SYSTEM_THREADS); + } @Override - int defaultReadPollers(Poller.Mode mode) { - assert mode == Poller.Mode.SYSTEM_THREADS; + int defaultReadPollers() { int ncpus = Runtime.getRuntime().availableProcessors(); return Math.max(Integer.highestOneBit(ncpus / 8), 1); } @@ -46,13 +54,15 @@ class DefaultPollerProvider extends PollerProvider { @Override Poller readPoller(boolean subPoller) throws IOException { - assert !subPoller; + if (subPoller) + throw new UnsupportedOperationException(); return new WEPollPoller(true); } @Override Poller writePoller(boolean subPoller) throws IOException { - assert !subPoller; + if (subPoller) + throw new UnsupportedOperationException(); return new WEPollPoller(false); } } diff --git a/src/java.base/windows/classes/sun/nio/ch/NativeThread.java b/src/java.base/windows/classes/sun/nio/ch/NativeThread.java index 1870c95494f..28d8c6303d9 100644 --- a/src/java.base/windows/classes/sun/nio/ch/NativeThread.java +++ b/src/java.base/windows/classes/sun/nio/ch/NativeThread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -25,47 +25,29 @@ package sun.nio.ch; - -// Signalling operations on native threads +import java.util.concurrent.locks.LockSupport; public class NativeThread { - private static final long VIRTUAL_THREAD_ID = -1L; + private NativeThread() { } /** - * Returns the id of the current native thread if the platform can signal - * native threads, 0 if the platform can not signal native threads, or - * -1L if the current thread is a virtual thread. + * Returns the Thread to signal the current thread or {@code null} if the current + * thread cannot be signalled. */ - public static long current() { - if (Thread.currentThread().isVirtual()) { - return VIRTUAL_THREAD_ID; + public static Thread threadToSignal() { + Thread thread = Thread.currentThread(); + return thread.isVirtual() ? thread : null; + } + + /** + * Signals the given thread. + * @throws UnsupportedOperationException is not supported + */ + public static void signal(Thread thread) { + if (thread.isVirtual()) { + LockSupport.unpark(thread); } else { - // no support for signalling threads on Windows - return 0; + throw new UnsupportedOperationException(); } } - - /** - * Signals the given native thread. - * - * @throws IllegalArgumentException if tid is not a token to a native thread - */ - static void signal(long tid) { - throw new UnsupportedOperationException(); - } - - /** - * Returns true the tid is the id of a native thread. - */ - static boolean isNativeThread(long tid) { - return false; - } - - /** - * Returns true if tid is -1L. - * @see #current() - */ - static boolean isVirtualThread(long tid) { - return (tid == VIRTUAL_THREAD_ID); - } } diff --git a/src/java.base/windows/classes/sun/nio/ch/WEPollPoller.java b/src/java.base/windows/classes/sun/nio/ch/WEPollPoller.java index 3db8d67acc6..9f575072f22 100644 --- a/src/java.base/windows/classes/sun/nio/ch/WEPollPoller.java +++ b/src/java.base/windows/classes/sun/nio/ch/WEPollPoller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -32,27 +32,41 @@ import static sun.nio.ch.WEPoll.*; */ class WEPollPoller extends Poller { private static final int MAX_EVENTS_TO_POLL = 256; - private static final int ENOENT = 2; private final long handle; private final int event; private final long address; WEPollPoller(boolean read) throws IOException { - this.handle = WEPoll.create(); + long handle = WEPoll.create(); + long address; + try { + address = WEPoll.allocatePollArray(MAX_EVENTS_TO_POLL); + } catch (Throwable e) { + WEPoll.close(handle); + throw e; + } + this.event = (read) ? EPOLLIN : EPOLLOUT; - this.address = WEPoll.allocatePollArray(MAX_EVENTS_TO_POLL); + this.handle = handle; + this.address = address; } @Override - void implRegister(int fdVal) throws IOException { + void close() { + WEPoll.close(handle); + WEPoll.freePollArray(address); + } + + @Override + void implStartPoll(int fdVal) throws IOException { int err = WEPoll.ctl(handle, EPOLL_CTL_ADD, fdVal, (event | EPOLLONESHOT)); if (err != 0) throw new IOException("epoll_ctl failed: " + err); } @Override - void implDeregister(int fdVal, boolean polled) { + void implStopPoll(int fdVal, boolean polled) { WEPoll.ctl(handle, EPOLL_CTL_DEL, fdVal, 0); } diff --git a/src/java.base/windows/native/libnio/ch/IOUtil.c b/src/java.base/windows/native/libnio/ch/IOUtil.c index 850c237d9e9..a6b81b7afec 100644 --- a/src/java.base/windows/native/libnio/ch/IOUtil.c +++ b/src/java.base/windows/native/libnio/ch/IOUtil.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -134,11 +134,10 @@ Java_sun_nio_ch_IOUtil_setfdVal(JNIEnv *env, jclass clazz, jobject fdo, jint val JNIEXPORT void JNICALL Java_sun_nio_ch_IOUtil_configureBlocking(JNIEnv *env, jclass clazz, - jobject fdo, jboolean blocking) + jint fd, jboolean blocking) { u_long argp; int result = 0; - jint fd = fdval(env, fdo); if (blocking == JNI_FALSE) { argp = SET_NONBLOCKING; diff --git a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpChannelImpl.java b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpChannelImpl.java index 485ebf13f2c..263aa58e098 100644 --- a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpChannelImpl.java +++ b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpChannelImpl.java @@ -84,9 +84,9 @@ public class SctpChannelImpl extends SctpChannel private final int fdVal; - /* IDs of native threads doing send and receive, for signalling */ - private volatile long receiverThread; - private volatile long senderThread; + /* Threads doing send and receive, for signalling */ + private volatile Thread receiverThread; + private volatile Thread senderThread; /* Lock held by current receiving or connecting thread */ private final Object receiveLock = new Object(); @@ -326,7 +326,7 @@ public class SctpChannelImpl extends SctpChannel private void receiverCleanup() throws IOException { synchronized (stateLock) { - receiverThread = 0; + receiverThread = null; if (state == ChannelState.KILLPENDING) kill(); } @@ -334,7 +334,7 @@ public class SctpChannelImpl extends SctpChannel private void senderCleanup() throws IOException { synchronized (stateLock) { - senderThread = 0; + senderThread = null; if (state == ChannelState.KILLPENDING) kill(); } @@ -367,7 +367,7 @@ public class SctpChannelImpl extends SctpChannel if (!isOpen()) { return false; } - receiverThread = NativeThread.current(); + receiverThread = NativeThread.threadToSignal(); } for (;;) { InetAddress ia = isa.getAddress(); @@ -472,7 +472,7 @@ public class SctpChannelImpl extends SctpChannel if (!isOpen()) { return false; } - receiverThread = NativeThread.current(); + receiverThread = NativeThread.threadToSignal(); } if (!isBlocking()) { connected = Net.pollConnect(fd, 0); @@ -484,7 +484,7 @@ public class SctpChannelImpl extends SctpChannel } } finally { synchronized (stateLock) { - receiverThread = 0; + receiverThread = null; if (state == ChannelState.KILLPENDING) { kill(); connected = false; @@ -541,10 +541,10 @@ public class SctpChannelImpl extends SctpChannel if (state != ChannelState.KILLED) SctpNet.preClose(fdVal); - if (receiverThread != 0) + if (receiverThread != null) NativeThread.signal(receiverThread); - if (senderThread != 0) + if (senderThread != null) NativeThread.signal(senderThread); if (!isRegistered()) @@ -644,7 +644,7 @@ public class SctpChannelImpl extends SctpChannel /* Postpone the kill if there is a waiting reader * or writer thread. */ - if (receiverThread == 0 && senderThread == 0) { + if (receiverThread == null && senderThread == null) { state = ChannelState.KILLED; SctpNet.close(fdVal); } else { @@ -743,7 +743,7 @@ public class SctpChannelImpl extends SctpChannel synchronized (stateLock) { if(!isOpen()) return null; - receiverThread = NativeThread.current(); + receiverThread = NativeThread.threadToSignal(); } do { @@ -936,7 +936,7 @@ public class SctpChannelImpl extends SctpChannel synchronized (stateLock) { if(!isOpen()) return 0; - senderThread = NativeThread.current(); + senderThread = NativeThread.threadToSignal(); } do { @@ -1031,7 +1031,7 @@ public class SctpChannelImpl extends SctpChannel ensureSendOpen(); SctpNet.shutdown(fdVal, -1); - if (senderThread != 0) + if (senderThread != null) NativeThread.signal(senderThread); isShutdown = true; } diff --git a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java index 31e83d72f96..c08c6dc88d0 100644 --- a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java +++ b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java @@ -81,9 +81,9 @@ public class SctpMultiChannelImpl extends SctpMultiChannel private final int fdVal; - /* IDs of native threads doing send and receives, for signalling */ - private volatile long receiverThread; - private volatile long senderThread; + /* Threads doing send and receives, for signalling */ + private volatile Thread receiverThread; + private volatile Thread senderThread; /* Lock held by current receiving thread */ private final Object receiveLock = new Object(); @@ -265,7 +265,7 @@ public class SctpMultiChannelImpl extends SctpMultiChannel private void receiverCleanup() throws IOException { synchronized (stateLock) { - receiverThread = 0; + receiverThread = null; if (state == ChannelState.KILLPENDING) kill(); } @@ -273,7 +273,7 @@ public class SctpMultiChannelImpl extends SctpMultiChannel private void senderCleanup() throws IOException { synchronized (stateLock) { - senderThread = 0; + senderThread = null; if (state == ChannelState.KILLPENDING) kill(); } @@ -290,10 +290,10 @@ public class SctpMultiChannelImpl extends SctpMultiChannel if (state != ChannelState.KILLED) SctpNet.preClose(fdVal); - if (receiverThread != 0) + if (receiverThread != null) NativeThread.signal(receiverThread); - if (senderThread != 0) + if (senderThread != null) NativeThread.signal(senderThread); if (!isRegistered()) @@ -378,7 +378,7 @@ public class SctpMultiChannelImpl extends SctpMultiChannel assert !isOpen() && !isRegistered(); /* Postpone the kill if there is a thread sending or receiving. */ - if (receiverThread == 0 && senderThread == 0) { + if (receiverThread == null && senderThread == null) { state = ChannelState.KILLED; SctpNet.close(fdVal); } else { @@ -484,7 +484,7 @@ public class SctpMultiChannelImpl extends SctpMultiChannel synchronized (stateLock) { if(!isOpen()) return null; - receiverThread = NativeThread.current(); + receiverThread = NativeThread.threadToSignal(); } do { @@ -765,7 +765,7 @@ public class SctpMultiChannelImpl extends SctpMultiChannel synchronized (stateLock) { if(!isOpen()) return 0; - senderThread = NativeThread.current(); + senderThread = NativeThread.threadToSignal(); /* Determine what address or association to send to */ Association assoc = messageInfo.association(); diff --git a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java index 4b2be742c6d..f72e0938eb5 100644 --- a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java +++ b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 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 @@ -58,8 +58,8 @@ public class SctpServerChannelImpl extends SctpServerChannel private final int fdVal; - /* IDs of native thread doing accept, for signalling */ - private volatile long thread; + /* thread doing accept, for signalling */ + private volatile Thread thread; /* Lock held by thread currently blocked in this channel */ private final Object lock = new Object(); @@ -200,7 +200,7 @@ public class SctpServerChannelImpl extends SctpServerChannel private void acceptCleanup() throws IOException { synchronized (stateLock) { - thread = 0; + thread = null; if (state == ChannelState.KILLPENDING) kill(); } @@ -222,7 +222,7 @@ public class SctpServerChannelImpl extends SctpServerChannel begin(); if (!isOpen()) return null; - thread = NativeThread.current(); + thread = NativeThread.threadToSignal(); for (;;) { n = Net.accept(fd, newfd, isaa); if ((n == IOStatus.INTERRUPTED) && isOpen()) @@ -253,7 +253,7 @@ public class SctpServerChannelImpl extends SctpServerChannel synchronized (stateLock) { if (state != ChannelState.KILLED) SctpNet.preClose(fdVal); - if (thread != 0) + if (thread != null) NativeThread.signal(thread); if (!isRegistered()) kill(); @@ -273,7 +273,7 @@ public class SctpServerChannelImpl extends SctpServerChannel assert !isOpen() && !isRegistered(); // Postpone the kill if there is a thread in accept - if (thread == 0) { + if (thread == null) { state = ChannelState.KILLED; SctpNet.close(fdVal); } else { diff --git a/test/jdk/java/net/vthread/BlockingSocketOps.java b/test/jdk/java/net/vthread/BlockingSocketOps.java index ef58e06b915..d3db734f36a 100644 --- a/test/jdk/java/net/vthread/BlockingSocketOps.java +++ b/test/jdk/java/net/vthread/BlockingSocketOps.java @@ -35,6 +35,7 @@ * @library /test/lib * @run junit/othervm -Djdk.pollerMode=1 BlockingSocketOps * @run junit/othervm -Djdk.pollerMode=2 BlockingSocketOps + * @run junit/othervm -Djdk.pollerMode=3 BlockingSocketOps */ /* diff --git a/test/jdk/java/nio/channels/vthread/BlockingChannelOps.java b/test/jdk/java/nio/channels/vthread/BlockingChannelOps.java index eb2229d927a..7e934301892 100644 --- a/test/jdk/java/nio/channels/vthread/BlockingChannelOps.java +++ b/test/jdk/java/nio/channels/vthread/BlockingChannelOps.java @@ -35,6 +35,7 @@ * @library /test/lib * @run junit/othervm/timeout=480 -Djdk.pollerMode=1 BlockingChannelOps * @run junit/othervm/timeout=480 -Djdk.pollerMode=2 BlockingChannelOps + * @run junit/othervm/timeout=480 -Djdk.pollerMode=3 BlockingChannelOps */ /* diff --git a/test/jdk/java/nio/channels/vthread/SelectorOps.java b/test/jdk/java/nio/channels/vthread/SelectorOps.java index 81821a85791..11fe98a3a01 100644 --- a/test/jdk/java/nio/channels/vthread/SelectorOps.java +++ b/test/jdk/java/nio/channels/vthread/SelectorOps.java @@ -34,6 +34,7 @@ * @library /test/lib * @run junit/othervm/native -Djdk.pollerMode=1 --enable-native-access=ALL-UNNAMED SelectorOps * @run junit/othervm/native -Djdk.pollerMode=2 --enable-native-access=ALL-UNNAMED SelectorOps + * @run junit/othervm/native -Djdk.pollerMode=3 --enable-native-access=ALL-UNNAMED SelectorOps */ /* From 986d3772248098c0ba845861611a5a4ceb7b645a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eirik=20Bj=C3=B8rsn=C3=B8s?= Date: Fri, 6 Feb 2026 17:06:04 +0000 Subject: [PATCH 160/215] 8376533: Remove test dependencies on ReferenceQueue$Lock in preparation for JDK-8376477 Reviewed-by: rriggs, shade, cjplummer --- .../hotspot/jtreg/serviceability/sa/ClhsdbInspect.java | 8 ++++---- .../jtreg/serviceability/sa/LingeredAppWithLock.java | 8 +++++++- .../ConcurrentHashMap/ConcurrentAssociateTest.java | 10 +++------- test/jdk/java/util/concurrent/Phaser/Basic.java | 6 ++---- .../bench/jdk/internal/jrtfs/ImageReaderBenchmark.java | 6 ++---- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbInspect.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbInspect.java index e7531226d2d..553706e502d 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbInspect.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbInspect.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 @@ -52,7 +52,7 @@ public class ClhsdbInspect { System.out.println("Started LingeredApp with pid " + theApp.getPid()); // Run the 'jstack -v' command to get the address of a Method*, - // the oop address of a java.lang.ref.ReferenceQueue$Lock + // the oop address of a LingeredAppWithLock$NestedLock // and the oop address of a java.lang.Class object List cmds = List.of("jstack -v"); @@ -63,8 +63,8 @@ public class ClhsdbInspect { tokensMap.put("(a java.lang.Class for LingeredAppWithLock)", "instance of Oop for java/lang/Class"); tokensMap.put("Method*=", "Type is Method"); - tokensMap.put("(a java.lang.ref.ReferenceQueue$Lock)", - "instance of Oop for java/lang/ref/ReferenceQueue\\$Lock"); + tokensMap.put("(a LingeredAppWithLock$NestedLock)", + "instance of Oop for LingeredAppWithLock\\$NestedLock"); String[] lines = jstackOutput.split("\\R"); diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java index 4319d576590..9a51aef75ce 100644 --- a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, 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 @@ -27,6 +27,8 @@ import jdk.test.lib.apps.LingeredApp; public class LingeredAppWithLock extends LingeredApp { private static Object lockObj = new Object(); + private static class NestedLock {} + public static void lockMethod(Object lock) { synchronized (lock) { try { @@ -50,12 +52,14 @@ public class LingeredAppWithLock extends LingeredApp { public static void main(String args[]) { Thread classLock1 = new Thread(() -> lockMethod(LingeredAppWithLock.class)); Thread classLock2 = new Thread(() -> lockMethod(LingeredAppWithLock.class)); + Thread nestedClassLock = new Thread(() -> lockMethod(new NestedLock())); Thread objectLock = new Thread(() -> lockMethod(classLock1)); Thread primitiveLock = new Thread(() -> lockMethod(int.class)); Thread objectWait = new Thread(() -> waitMethod()); classLock1.start(); classLock2.start(); + nestedClassLock.start(); objectLock.start(); primitiveLock.start(); objectWait.start(); @@ -65,6 +69,8 @@ public class LingeredAppWithLock extends LingeredApp { classLock1.getState() != Thread.State.TIMED_WAITING) || (classLock2.getState() != Thread.State.BLOCKED && classLock2.getState() != Thread.State.TIMED_WAITING) || + (nestedClassLock.getState() != Thread.State.BLOCKED && + nestedClassLock.getState() != Thread.State.TIMED_WAITING) || (objectLock.getState() != Thread.State.TIMED_WAITING) || (primitiveLock.getState() != Thread.State.TIMED_WAITING) || (objectWait.getState() != Thread.State.TIMED_WAITING)) { diff --git a/test/jdk/java/util/concurrent/ConcurrentHashMap/ConcurrentAssociateTest.java b/test/jdk/java/util/concurrent/ConcurrentHashMap/ConcurrentAssociateTest.java index e6afff5b329..014272a8f96 100644 --- a/test/jdk/java/util/concurrent/ConcurrentHashMap/ConcurrentAssociateTest.java +++ b/test/jdk/java/util/concurrent/ConcurrentHashMap/ConcurrentAssociateTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, 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 @@ -171,13 +171,9 @@ public class ConcurrentAssociateTest { String lockName; if ("Signal Dispatcher".equals(name)) continue; - if ("Reference Handler".equals(name) - && (lockName = info.getLockName()) != null - && lockName.startsWith("java.lang.ref.Reference$Lock")) + if ("Reference Handler".equals(name)) continue; - if ("Finalizer".equals(name) - && (lockName = info.getLockName()) != null - && lockName.startsWith("java.lang.ref.ReferenceQueue$Lock")) + if ("Finalizer".equals(name)) continue; System.err.print(info); } diff --git a/test/jdk/java/util/concurrent/Phaser/Basic.java b/test/jdk/java/util/concurrent/Phaser/Basic.java index 2ea09dced99..500e106891b 100644 --- a/test/jdk/java/util/concurrent/Phaser/Basic.java +++ b/test/jdk/java/util/concurrent/Phaser/Basic.java @@ -434,11 +434,9 @@ public class Basic { String name = info.getThreadName(); if ("Signal Dispatcher".equals(name)) continue; - if ("Reference Handler".equals(name) - && info.getLockName().startsWith("java.lang.ref.Reference$Lock")) + if ("Reference Handler".equals(name)) continue; - if ("Finalizer".equals(name) - && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock")) + if ("Finalizer".equals(name)) continue; if (name.startsWith("process reaper")) continue; diff --git a/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java b/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java index 1b89b510fae..b6876e66d98 100644 --- a/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.java +++ b/test/micro/org/openjdk/bench/jdk/internal/jrtfs/ImageReaderBenchmark.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 @@ -447,7 +447,6 @@ public class ImageReaderBenchmark { "/modules/java.base/jdk/internal/access/JavaLangRefAccess.class", "/modules/java.base/java/lang/ref/ReferenceQueue.class", "/modules/java.base/java/lang/ref/ReferenceQueue$Null.class", - "/modules/java.base/java/lang/ref/ReferenceQueue$Lock.class", "/modules/java.base/jdk/internal/access/JavaLangAccess.class", "/modules/java.base/jdk/internal/util/SystemProps.class", "/modules/java.base/jdk/internal/util/SystemProps$Raw.class", @@ -1073,6 +1072,5 @@ public class ImageReaderBenchmark { "/modules/java.base/java/nio/charset/CoderResult.class", "/modules/java.base/java/util/IdentityHashMap$IdentityHashMapIterator.class", "/modules/java.base/java/util/IdentityHashMap$KeyIterator.class", - "/modules/java.base/java/lang/Shutdown.class", - "/modules/java.base/java/lang/Shutdown$Lock.class"); + "/modules/java.base/java/lang/Shutdown.class"); } From eec76d7b8c4c8a64593d85338225906c188f679c Mon Sep 17 00:00:00 2001 From: Kelvin Nilsen Date: Sat, 7 Feb 2026 00:57:23 +0000 Subject: [PATCH 161/215] 8377180: Shenandoah: make escalation from degen to full more conservative Reviewed-by: wkemper, xpeng --- .../shenandoah/shenandoahCollectorPolicy.hpp | 19 ++++++------------- .../gc/shenandoah/shenandoahDegeneratedGC.cpp | 2 +- .../test_shenandoahCollectorPolicy.cpp | 12 ++++++------ 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp index 5fe90f64f98..1166333ae3a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp @@ -63,17 +63,10 @@ private: public: // The most common scenario for lack of good progress following a degenerated GC is an accumulation of floating - // garbage during the most recently aborted concurrent GC effort. With generational GC, it is far more effective to + // garbage during the most recently aborted concurrent GC effort. Usually, it is far more effective to // reclaim this floating garbage with another degenerated cycle (which focuses on young generation and might require - // a pause of 200 ms) rather than a full GC cycle (which may require over 2 seconds with a 10 GB old generation). - // - // In generational mode, we'll only upgrade to full GC if we've done two degen cycles in a row and both indicated - // bad progress. In non-generational mode, we'll preserve the original behavior, which is to upgrade to full - // immediately following a degenerated cycle with bad progress. This preserves original behavior of non-generational - // Shenandoah to avoid introducing "surprising new behavior." It also makes less sense with non-generational - // Shenandoah to replace a full GC with a degenerated GC, because both have similar pause times in non-generational - // mode. - static constexpr size_t GENERATIONAL_CONSECUTIVE_BAD_DEGEN_PROGRESS_THRESHOLD = 2; + // a pause of 200 ms) rather than a full GC cycle (which may require multiple seconds with a 10 GB old generation). + static constexpr size_t CONSECUTIVE_BAD_DEGEN_PROGRESS_THRESHOLD = 2; ShenandoahCollectorPolicy(); @@ -117,9 +110,9 @@ public: return _consecutive_degenerated_gcs; } - // Genshen will only upgrade to a full gc after the configured number of futile degenerated cycles. - bool generational_should_upgrade_degenerated_gc() const { - return _consecutive_degenerated_gcs_without_progress >= GENERATIONAL_CONSECUTIVE_BAD_DEGEN_PROGRESS_THRESHOLD; + // Only upgrade to a full gc after the configured number of futile degenerated cycles. + bool should_upgrade_degenerated_gc() const { + return _consecutive_degenerated_gcs_without_progress >= CONSECUTIVE_BAD_DEGEN_PROGRESS_THRESHOLD; } static bool is_allocation_failure(GCCause::Cause cause); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp index 333bdbc6e72..99776e38bfe 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp @@ -314,7 +314,7 @@ void ShenandoahDegenGC::op_degenerated() { if (progress) { heap->notify_gc_progress(); _generation->heuristics()->record_degenerated(); - } else if (!heap->mode()->is_generational() || policy->generational_should_upgrade_degenerated_gc()) { + } else if (policy->should_upgrade_degenerated_gc()) { // Upgrade to full GC, register full-GC impact on heuristics. op_degenerated_futile(); } else { diff --git a/test/hotspot/gtest/gc/shenandoah/test_shenandoahCollectorPolicy.cpp b/test/hotspot/gtest/gc/shenandoah/test_shenandoahCollectorPolicy.cpp index b5c974f65ad..70d458ec5e5 100644 --- a/test/hotspot/gtest/gc/shenandoah/test_shenandoahCollectorPolicy.cpp +++ b/test/hotspot/gtest/gc/shenandoah/test_shenandoahCollectorPolicy.cpp @@ -28,7 +28,7 @@ TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_sanity) { ShenandoahCollectorPolicy policy; EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL); - EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false); + EXPECT_EQ(policy.should_upgrade_degenerated_gc(), false); } TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_no_upgrade) { @@ -36,7 +36,7 @@ TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_no_upgrade) { policy.record_degenerated(true, true, true); policy.record_degenerated(true, true, true); EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL); - EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false); + EXPECT_EQ(policy.should_upgrade_degenerated_gc(), false); } TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_upgrade) { @@ -44,7 +44,7 @@ TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_upgrade) { policy.record_degenerated(true, true, false); policy.record_degenerated(true, true, false); EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL); - EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), true); + EXPECT_EQ(policy.should_upgrade_degenerated_gc(), true); } TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_reset_progress) { @@ -52,7 +52,7 @@ TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_reset_progress) { policy.record_degenerated(true, true, false); policy.record_degenerated(true, true, true); EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 2UL); - EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false); + EXPECT_EQ(policy.should_upgrade_degenerated_gc(), false); } TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_full_reset) { @@ -60,7 +60,7 @@ TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_full_reset) { policy.record_degenerated(true, true, false); policy.record_success_full(); EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL); - EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false); + EXPECT_EQ(policy.should_upgrade_degenerated_gc(), false); } TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_reset) { @@ -68,5 +68,5 @@ TEST(ShenandoahCollectorPolicyTest, track_degen_cycles_reset) { policy.record_degenerated(true, true, false); policy.record_success_concurrent(true, true); EXPECT_EQ(policy.consecutive_degenerated_gc_count(), 0UL); - EXPECT_EQ(policy.generational_should_upgrade_degenerated_gc(), false); + EXPECT_EQ(policy.should_upgrade_degenerated_gc(), false); } From 4c322344cd08608ae4d4e9bf99e7333ac8009f26 Mon Sep 17 00:00:00 2001 From: SendaoYan Date: Sat, 7 Feb 2026 01:59:33 +0000 Subject: [PATCH 162/215] 8377018: Convert java/nio/file/DirectoryStream/SecureDS.java to junit Reviewed-by: bpb, alanb --- .../nio/file/DirectoryStream/SecureDS.java | 167 +++++++----------- 1 file changed, 67 insertions(+), 100 deletions(-) diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index a115d56c52f..870a84a8927 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -21,20 +21,13 @@ * questions. */ -/* @test id=tmp +/* @test * @bug 4313887 6838333 8343020 8357425 * @summary Unit test for java.nio.file.SecureDirectoryStream * @requires (os.family == "linux" | os.family == "mac" | os.family == "aix") * @library .. /test/lib - * @build jdk.test.lib.Platform jtreg.SkippedException - * @run main SecureDS - */ - -/* @test id=cwd - * @requires (os.family == "linux" | os.family == "mac" | os.family == "aix") - * @library .. /test/lib - * @build jdk.test.lib.Platform jtreg.SkippedException - * @run main SecureDS cwd + * @build jdk.test.lib.Platform + * @run junit SecureDS */ import java.nio.file.*; @@ -46,14 +39,22 @@ import java.io.IOException; import java.util.*; import jdk.test.lib.Platform; -import jtreg.SkippedException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class SecureDS { static boolean supportsSymbolicLinks; - public static void main(String[] args) throws IOException { + @ParameterizedTest + @ValueSource(strings = {"tmp","cwd"}) + public void testSecureDS(String mode) throws IOException { Path dir; - if (args.length > 0 && args[0].equals("cwd")) { + if (mode.equals("cwd")) { dir = TestUtil.createTemporaryDirectory(System.getProperty("user.dir")); } else { dir = TestUtil.createTemporaryDirectory(); @@ -61,9 +62,7 @@ public class SecureDS { try { DirectoryStream stream = newDirectoryStream(dir); stream.close(); - if (!(stream instanceof SecureDirectoryStream)) { - throw new AssertionError("SecureDirectoryStream not supported."); - } + assumeTrue(stream instanceof SecureDirectoryStream); supportsSymbolicLinks = TestUtil.supportsSymbolicLinks(dir); @@ -106,7 +105,7 @@ public class SecureDS { // Test: iterate over all entries int count = 0; for (Path entry: stream) { count++; } - assertTrue(count == (supportsSymbolicLinks ? 4 : 2)); + assertEquals((supportsSymbolicLinks ? 4 : 2), count); // Test: getFileAttributeView to access directory's attributes assertTrue(stream @@ -155,13 +154,12 @@ public class SecureDS { stream.newByteChannel(fileEntry, opts).close(); if (supportsSymbolicLinks) { stream.newByteChannel(link1Entry, opts).close(); - try { + assertThrows(IOException.class, () -> { Set mixed = new HashSet<>(); mixed.add(READ); mixed.add(NOFOLLOW_LINKS); stream.newByteChannel(link1Entry, mixed).close(); - shouldNotGetHere(); - } catch (IOException x) { } + }); } // Test: newDirectoryStream @@ -169,11 +167,10 @@ public class SecureDS { stream.newDirectoryStream(dirEntry, LinkOption.NOFOLLOW_LINKS).close(); if (supportsSymbolicLinks) { stream.newDirectoryStream(link2Entry).close(); - try { + assertThrows(IOException.class, () -> { stream.newDirectoryStream(link2Entry, LinkOption.NOFOLLOW_LINKS) .close(); - shouldNotGetHere(); - } catch (IOException x) { } + }); } // Test: delete @@ -201,10 +198,10 @@ public class SecureDS { // Test setting permission on directory with no permissions setPosixFilePermissions(aDir, noperms); - assertTrue(getPosixFilePermissions(aDir).equals(noperms)); + assertEquals(noperms, getPosixFilePermissions(aDir)); PosixFileAttributeView view = stream.getFileAttributeView(PosixFileAttributeView.class); view.setPermissions(permsDir); - assertTrue(getPosixFilePermissions(aDir).equals(permsDir)); + assertEquals(permsDir, getPosixFilePermissions(aDir)); if (supportsSymbolicLinks) { // Create a file and a link to the file @@ -218,22 +215,21 @@ public class SecureDS { // Test following link to file view = stream.getFileAttributeView(link, PosixFileAttributeView.class); view.setPermissions(noperms); - assertTrue(getPosixFilePermissions(file).equals(noperms)); - assertTrue(getPosixFilePermissions(link, NOFOLLOW_LINKS).equals(permsLink)); + assertEquals(noperms, getPosixFilePermissions(file)); + assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); view.setPermissions(permsFile); - assertTrue(getPosixFilePermissions(file).equals(permsFile)); - assertTrue(getPosixFilePermissions(link, NOFOLLOW_LINKS).equals(permsLink)); - + assertEquals(permsFile, getPosixFilePermissions(file)); + assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); // Symbolic link permissions do not apply on Linux if (!Platform.isLinux()) { // Test not following link to file view = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS); view.setPermissions(noperms); - assertTrue(getPosixFilePermissions(file).equals(permsFile)); - assertTrue(getPosixFilePermissions(link, NOFOLLOW_LINKS).equals(noperms)); + assertEquals(permsFile, getPosixFilePermissions(file)); + assertEquals(noperms, getPosixFilePermissions(link, NOFOLLOW_LINKS)); view.setPermissions(permsLink); - assertTrue(getPosixFilePermissions(file).equals(permsFile)); - assertTrue(getPosixFilePermissions(link, NOFOLLOW_LINKS).equals(permsLink)); + assertEquals(permsFile, getPosixFilePermissions(file)); + assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); } delete(link); @@ -295,10 +291,9 @@ public class SecureDS { SecureDirectoryStream ts = (SecureDirectoryStream)newDirectoryStream(testDir); createFile(dir1.resolve(fileEntry)); - try { + assertThrows(AtomicMoveNotSupportedException.class, () -> { stream1.move(fileEntry, ts, target); - shouldNotGetHere(); - } catch (AtomicMoveNotSupportedException x) { } + }); ts.close(); stream1.deleteFile(fileEntry); } @@ -316,18 +311,13 @@ public class SecureDS { try { sds.move(file, null, file); } catch (AtomicMoveNotSupportedException e) { - if (Files.getFileStore(cwd).equals(Files.getFileStore(dir))) { - // re-throw if move between same volume - throw e; - } else { - throw new SkippedException( - "java.nio.file.AtomicMoveNotSupportedException"); - } + assumeTrue(Files.getFileStore(cwd).equals(Files.getFileStore(dir))); + // re-throw if move between same volume + throw e; } - if (!TEXT.equals(Files.readString(result))) - throw new RuntimeException(result + " content incorrect"); + assertEquals(TEXT, Files.readString(result), result + " content incorrect"); } else { - throw new RuntimeException("Not a SecureDirectoryStream"); + fail("Not a SecureDirectoryStream"); } } finally { boolean fileDeleted = Files.deleteIfExists(filepath); @@ -348,82 +338,59 @@ public class SecureDS { (SecureDirectoryStream)newDirectoryStream(dir); // NullPointerException - try { + assertThrows(NullPointerException.class, () -> { stream.getFileAttributeView(null); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.getFileAttributeView(null, BasicFileAttributeView.class); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.getFileAttributeView(file, null); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.newByteChannel(null, EnumSet.of(CREATE,WRITE)); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.newByteChannel(null, EnumSet.of(CREATE,WRITE,null)); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.newByteChannel(file, null); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.move(null, stream, file); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.move(file, stream, null); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.newDirectoryStream(null); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.deleteFile(null); - shouldNotGetHere(); - } catch (NullPointerException x) { } - try { + }); + assertThrows(NullPointerException.class, () -> { stream.deleteDirectory(null); - shouldNotGetHere(); - } catch (NullPointerException x) { } + }); // close stream stream.close(); stream.close(); // should be no-op // ClosedDirectoryStreamException - try { + assertThrows(ClosedDirectoryStreamException.class, () -> { stream.newDirectoryStream(file); - shouldNotGetHere(); - } catch (ClosedDirectoryStreamException x) { } - try { + }); + assertThrows(ClosedDirectoryStreamException.class, () -> { stream.newByteChannel(file, EnumSet.of(READ)); - shouldNotGetHere(); - } catch (ClosedDirectoryStreamException x) { } - try { + }); + assertThrows(ClosedDirectoryStreamException.class, () -> { stream.move(file, stream, file); - shouldNotGetHere(); - } catch (ClosedDirectoryStreamException x) { } - try { + }); + assertThrows(ClosedDirectoryStreamException.class, () -> { stream.deleteFile(file); - shouldNotGetHere(); - } catch (ClosedDirectoryStreamException x) { } + }); // clean-up delete(dir.resolve(file)); } - - static void assertTrue(boolean b) { - if (!b) throw new RuntimeException("Assertion failed"); - } - - static void shouldNotGetHere() { - assertTrue(false); - } } From 40bf0870f788c142f0eb1c2bfbda540ae4a93a08 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sat, 7 Feb 2026 03:18:11 +0000 Subject: [PATCH 163/215] 8377392: jpackage: Fix member function called from the CfgFile.Referencies compact canonical constructor Reviewed-by: liach, almatvee --- .../share/classes/jdk/jpackage/internal/CfgFile.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CfgFile.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CfgFile.java index 9cb9fb5cba0..7958dcdef2d 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CfgFile.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/CfgFile.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 @@ -124,21 +124,19 @@ final class CfgFile { Files.write(cfgFile, (Iterable) lines::iterator); } - private record Referencies(Path appModsDirectory) { + private record Referencies(Path appModsDirectory, Path appDirectory) { Referencies { - if (!appModsDirectory.startsWith(appDirectory())) { + if (!appModsDirectory.startsWith(appDirectory)) { throw new IllegalArgumentException(); } } Referencies(ApplicationLayout appLayout) { - this(Path.of("$APPDIR").resolve(appLayout.appModsDirectory().getFileName())); + this(BASEDIR.resolve(appLayout.appModsDirectory().getFileName()), BASEDIR); } - Path appDirectory() { - return Path.of("$APPDIR"); - } + private static final Path BASEDIR = Path.of("$APPDIR"); } private final LauncherStartupInfo startupInfo; From 5152fdcd490412025ba5f608378982abc1eadc07 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sat, 7 Feb 2026 03:27:23 +0000 Subject: [PATCH 164/215] 8377331: jpackage: improve sign errors reporting Reviewed-by: almatvee --- .../jdk/jpackage/internal/AppImageSigner.java | 21 +--- .../jdk/jpackage/internal/Codesign.java | 20 +-- .../resources/MacResources.properties | 1 - .../jdk/jpackage/internal/cli/Main.java | 8 +- .../test/FailedCommandErrorValidator.java | 115 ++++++++++++++++++ .../jdk/jpackage/test/JPackageCommand.java | 9 +- .../helpers/jdk/jpackage/test/MacHelper.java | 16 +++ .../helpers/jdk/jpackage/test/TKit.java | 43 ++++++- .../jdk/jpackage/internal/cli/MainTest.java | 4 +- .../tools/jpackage/macosx/MacSignTest.java | 79 ++++++------ 10 files changed, 243 insertions(+), 73 deletions(-) create mode 100644 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FailedCommandErrorValidator.java diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java index 19ff78f174e..c908ec7447c 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigner.java @@ -24,7 +24,6 @@ */ package jdk.jpackage.internal; -import static java.util.stream.Collectors.joining; import static jdk.jpackage.internal.MacPackagingPipeline.APPLICATION_LAYOUT; import static jdk.jpackage.internal.model.MacPackage.RUNTIME_BUNDLE_LAYOUT; import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer; @@ -40,7 +39,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Stream; import jdk.jpackage.internal.Codesign.CodesignException; import jdk.jpackage.internal.model.Application; import jdk.jpackage.internal.model.ApplicationLayout; @@ -63,9 +61,10 @@ final class AppImageSigner { throw handleCodesignException(app, ex); } catch (ExceptionBox ex) { if (ex.getCause() instanceof CodesignException codesignEx) { - handleCodesignException(app, codesignEx); + throw handleCodesignException(app, codesignEx); + } else { + throw ex; } - throw ex; } }); } @@ -165,13 +164,9 @@ final class AppImageSigner { } } - private static CodesignException handleCodesignException(MacApplication app, CodesignException ex) { - // Log output of "codesign" in case of error. It should help - // user to diagnose issues when using --mac-app-image-sign-identity. - // In addition add possible reason for failure. For example - // "--app-content" can fail "codesign". - + private static IOException handleCodesignException(MacApplication app, CodesignException ex) { if (!app.contentDirSources().isEmpty()) { + // Additional content may cause signing error. Log.info(I18N.getString("message.codesign.failed.reason.app.content")); } @@ -182,11 +177,7 @@ final class AppImageSigner { Log.info(I18N.getString("message.codesign.failed.reason.xcode.tools")); } - // Log "codesign" output - Log.info(I18N.format("error.tool.failed.with.output", "codesign")); - Log.info(Stream.of(ex.getOutput()).collect(joining("\n")).strip()); - - return ex; + return ex.getCause(); } private static boolean isXcodeDevToolsInstalled() { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java index a7cd17b06b9..984202bbfaf 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/Codesign.java @@ -34,22 +34,21 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier; - +import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; public final class Codesign { public static final class CodesignException extends Exception { - CodesignException(String[] output) { - this.output = output; + CodesignException(UnexpectedExitCodeException cause) { + super(Objects.requireNonNull(cause)); } - String[] getOutput() { - return output; + @Override + public UnexpectedExitCodeException getCause() { + return (UnexpectedExitCodeException)super.getCause(); } - private final String[] output; - private static final long serialVersionUID = 1L; } @@ -96,9 +95,10 @@ public final class Codesign { var exec = Executor.of(cmdline).args(path.toString()).saveOutput(true); configureExecutor.ifPresent(configure -> configure.accept(exec)); - var result = exec.execute(); - if (result.getExitCode() != 0) { - throw new CodesignException(result.getOutput().toArray(String[]::new)); + try { + exec.execute().expectExitCode(0); + } catch (UnexpectedExitCodeException ex) { + throw new CodesignException(ex); } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties index 0237d49f399..e1b154c5933 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties @@ -28,7 +28,6 @@ error.certificate.expired=Certificate expired {0} error.cert.not.found=No certificate found matching [{0}] using keychain [{1}] error.multiple.certs.found=Multiple certificates matching name [{0}] found in keychain [{1}] error.app-image.mac-sign.required=--mac-sign option is required with predefined application image and with type [app-image] -error.tool.failed.with.output="{0}" failed with following output: error.invalid-runtime-image-missing-file=Runtime image "{0}" is missing "{1}" file error.invalid-runtime-image-bin-dir=Runtime image "{0}" should not contain "bin" folder error.invalid-runtime-image-bin-dir.advice=Use --strip-native-commands jlink option when generating runtime image used with {0} option diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java index 562a0d2d3c1..73b4850344b 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java @@ -271,11 +271,9 @@ public final class Main { } messagePrinter.accept(I18N.format("message.error-header", msg)); - if (!verbose) { - messagePrinter.accept(I18N.format("message.failed-command-output-header")); - try (var lines = new BufferedReader(new StringReader(commandOutput)).lines()) { - lines.forEach(messagePrinter); - } + messagePrinter.accept(I18N.format("message.failed-command-output-header")); + try (var lines = new BufferedReader(new StringReader(commandOutput)).lines()) { + lines.forEach(messagePrinter); } } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FailedCommandErrorValidator.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FailedCommandErrorValidator.java new file mode 100644 index 00000000000..ab644c36a5c --- /dev/null +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/FailedCommandErrorValidator.java @@ -0,0 +1,115 @@ +/* + * 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.jpackage.test; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; + +/** + * Validates failed command error in jpackage's output. + */ +public final class FailedCommandErrorValidator { + + public FailedCommandErrorValidator(Pattern cmdlinePattern) { + this.cmdlinePattern = Objects.requireNonNull(cmdlinePattern); + } + + public TKit.TextStreamVerifier.Group createGroup() { + var asPredicate = cmdlinePattern.asPredicate(); + + var errorMessage = exitCode().map(v -> { + return JPackageStringBundle.MAIN.cannedFormattedString("error.command-failed-unexpected-exit-code", v, ""); + }).orElseGet(() -> { + return JPackageStringBundle.MAIN.cannedFormattedString("error.command-failed-unexpected-output", ""); + }); + + var errorMessageWithPrefix = JPackageCommand.makeError(errorMessage).getValue(); + + var group = TKit.TextStreamVerifier.group(); + + group.add(TKit.assertTextStream(cmdlinePattern.pattern()).predicate(line -> { + if (line.startsWith(errorMessageWithPrefix)) { + line = line.substring(errorMessageWithPrefix.length()); + return asPredicate.test(line); + } else { + return false; + } + })); + + group.add(TKit.assertTextStream( + JPackageStringBundle.MAIN.cannedFormattedString("message.failed-command-output-header").getValue() + ).predicate(String::equals)); + + outputVerifier().ifPresent(group::add); + + return group; + } + + public void applyTo(JPackageCommand cmd) { + cmd.validateOutput(createGroup().create()); + } + + public FailedCommandErrorValidator validator(TKit.TextStreamVerifier.Group v) { + outputValidator = v; + return this; + } + + public FailedCommandErrorValidator validator(List validators) { + var group = TKit.TextStreamVerifier.group(); + validators.forEach(group::add); + return validator(group); + } + + public FailedCommandErrorValidator validators(TKit.TextStreamVerifier... validators) { + return validator(List.of(validators)); + } + + public FailedCommandErrorValidator output(List v) { + return validator(v.stream().map(TKit::assertTextStream).toList()); + } + + public FailedCommandErrorValidator output(String... output) { + return output(List.of(output)); + } + + public FailedCommandErrorValidator exitCode(int v) { + exitCode = v; + return this; + } + + private Optional exitCode() { + return Optional.ofNullable(exitCode); + } + + private Optional outputVerifier() { + return Optional.ofNullable(outputValidator); + } + + private final Pattern cmdlinePattern; + private TKit.TextStreamVerifier.Group outputValidator; + private Integer exitCode; +} diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index f81c35cea0b..8c7526be9f9 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -906,6 +906,11 @@ public class JPackageCommand extends CommandArguments { return this; } + public JPackageCommand validateOutput(TKit.TextStreamVerifier.Group group) { + group.tryCreate().ifPresent(this::validateOutput); + return this; + } + @FunctionalInterface public interface CannedArgument { public String value(JPackageCommand cmd); @@ -947,11 +952,11 @@ public class JPackageCommand extends CommandArguments { public JPackageCommand validateOutput(CannedFormattedString... str) { // Will look up the given errors in the order they are specified. - Stream.of(str).map(this::getValue) + validateOutput(Stream.of(str).map(this::getValue) .map(TKit::assertTextStream) .reduce(TKit.TextStreamVerifier.group(), TKit.TextStreamVerifier.Group::add, - TKit.TextStreamVerifier.Group::add).tryCreate().ifPresent(this::validateOutput); + TKit.TextStreamVerifier.Group::add)); return this; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java index dc1a7b3512b..1191cd02221 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -782,6 +782,10 @@ public final class MacHelper { return sign(cmd); } + public Optional optionName() { + return type.mapOptionName(certRequest.type()); + } + public List asCmdlineArgs() { String[] args = new String[2]; applyTo((optionName, optionValue) -> { @@ -791,6 +795,10 @@ public final class MacHelper { return List.of(args); } + public Optional passThrough() { + return optionName().map(Name::passThrough); + } + private void applyTo(BiConsumer sink) { type.mapOptionName(certRequest.type()).ifPresent(optionName -> { sink.accept(optionName.optionName(), optionValue()); @@ -886,6 +894,14 @@ public final class MacHelper { return signKeyOption.certRequest(); } + public Optional optionName() { + return signKeyOption.optionName(); + } + + public Optional passThrough() { + return signKeyOption.passThrough(); + } + public JPackageCommand addTo(JPackageCommand cmd) { Optional.ofNullable(cmd.getArgumentValue("--mac-signing-keychain")).ifPresentOrElse(configuredKeychain -> { if (!configuredKeychain.equals(keychain.name())) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java index 1639beadb28..7666d1e5167 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TKit.java @@ -65,6 +65,7 @@ import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.internal.util.OperatingSystem; @@ -1082,6 +1083,11 @@ public final class TKit { predicate(String::contains); } + TextStreamVerifier(Pattern value) { + this(Objects.requireNonNull(value).pattern()); + predicate(value.asPredicate()); + } + TextStreamVerifier(TextStreamVerifier other) { predicate = other.predicate; label = other.label; @@ -1091,6 +1097,10 @@ public final class TKit { value = other.value; } + public TextStreamVerifier copy() { + return new TextStreamVerifier(this); + } + public TextStreamVerifier label(String v) { label = v; return this; @@ -1101,6 +1111,13 @@ public final class TKit { return this; } + public TextStreamVerifier predicate(Predicate v) { + Objects.requireNonNull(v); + return predicate((str, _) -> { + return v.test(str); + }); + } + public TextStreamVerifier negate() { negate = true; return this; @@ -1116,7 +1133,12 @@ public final class TKit { return this; } - private String findMatch(Iterator lineIt) { + public TextStreamVerifier mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + + private String find(Iterator lineIt) { while (lineIt.hasNext()) { final var line = lineIt.next(); if (predicate.test(line, value)) { @@ -1131,7 +1153,7 @@ public final class TKit { } public void apply(Iterator lineIt) { - final String matchedStr = findMatch(lineIt); + final String matchedStr = find(lineIt); final String labelStr = Optional.ofNullable(label).orElse("output"); if (negate) { String msg = String.format( @@ -1180,6 +1202,11 @@ public final class TKit { return this; } + public Group mutate(Consumer mutator) { + mutator.accept(this); + return this; + } + public boolean isEmpty() { return verifiers.isEmpty(); } @@ -1226,6 +1253,18 @@ public final class TKit { return new TextStreamVerifier(what); } + public static TextStreamVerifier assertTextStream(Pattern what) { + return new TextStreamVerifier(what); + } + + public static Consumer> assertEndOfTextStream() { + return it -> { + var tail = new ArrayList(); + it.forEachRemaining(tail::add); + assertStringListEquals(List.of(), tail, "Check the end of the output"); + }; + } + public record PathSnapshot(List contentHashes) { public PathSnapshot { contentHashes.forEach(Objects::requireNonNull); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java index 79648260274..46de970a829 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java @@ -279,9 +279,7 @@ public class MainTest extends JUnitAdapter { expectedOutput.add(ExceptionFormatter.STACK_TRACE); } expectedOutput.add(expect.getValue()); - if (!verbose) { - expectedOutput.add(ExceptionFormatter.FAILED_COMMAND_OUTPUT); - } + expectedOutput.add(ExceptionFormatter.FAILED_COMMAND_OUTPUT); data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); } } diff --git a/test/jdk/tools/jpackage/macosx/MacSignTest.java b/test/jdk/tools/jpackage/macosx/MacSignTest.java index a824fdb0925..0be494ea469 100644 --- a/test/jdk/tools/jpackage/macosx/MacSignTest.java +++ b/test/jdk/tools/jpackage/macosx/MacSignTest.java @@ -22,22 +22,23 @@ */ import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY; +import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY_APP_IMAGE; import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_USER_FULL_NAME; import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME; -import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY_APP_IMAGE; import java.io.IOException; import java.nio.file.Files; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; +import java.util.regex.Pattern; import java.util.stream.Stream; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.CannedFormattedString; +import jdk.jpackage.test.FailedCommandErrorValidator; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.MacHelper; @@ -74,22 +75,32 @@ public class MacSignTest { Files.createDirectory(appContent); Files.createFile(appContent.resolve("file")); - final List expectedStrings = new ArrayList<>(); - expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("message.codesign.failed.reason.app.content")); + final var group = TKit.TextStreamVerifier.group(); - expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("error.tool.failed.with.output", "codesign")); + group.add(TKit.assertTextStream(JPackageStringBundle.MAIN.cannedFormattedString( + "message.codesign.failed.reason.app.content").getValue()).predicate(String::equals)); + + final var xcodeWarning = TKit.assertTextStream(JPackageStringBundle.MAIN.cannedFormattedString( + "message.codesign.failed.reason.xcode.tools").getValue()).predicate(String::equals); - final var xcodeWarning = JPackageStringBundle.MAIN.cannedFormattedString("message.codesign.failed.reason.xcode.tools"); if (!MacHelper.isXcodeDevToolsInstalled()) { - expectedStrings.add(xcodeWarning); + group.add(xcodeWarning); } - MacSign.withKeychain(keychain -> { + var keychain = SigningBase.StandardKeychain.MAIN.keychain(); - var signingKeyOption = new SignKeyOptionWithKeychain( - SIGN_KEY_IDENTITY, - SigningBase.StandardCertificateRequest.CODESIGN, - keychain); + var signingKeyOption = new SignKeyOptionWithKeychain( + SIGN_KEY_IDENTITY, + SigningBase.StandardCertificateRequest.CODESIGN, + keychain); + + new FailedCommandErrorValidator(Pattern.compile(String.format( + "/usr/bin/codesign -s %s -vvvv --timestamp --options runtime --prefix \\S+ --keychain %s --entitlements \\S+ \\S+", + Pattern.quote(String.format("'%s'", signingKeyOption.certRequest().name())), + Pattern.quote(keychain.name()) + ))).exitCode(1).createGroup().mutate(group::add); + + MacSign.withKeychain(_ -> { // --app-content and --type app-image // Expect `message.codesign.failed.reason.app.content` message in the log. @@ -97,51 +108,49 @@ public class MacSignTest { // To make jpackage fail, specify bad additional content. JPackageCommand.helloAppImage() .ignoreDefaultVerbose(true) - .validateOutput(expectedStrings.toArray(CannedFormattedString[]::new)) + .validateOutput(group.create()) .addArguments("--app-content", appContent) .mutate(signingKeyOption::addTo) .mutate(cmd -> { if (MacHelper.isXcodeDevToolsInstalled()) { // Check there is no warning about missing xcode command line developer tools. - cmd.validateOutput(TKit.assertTextStream(xcodeWarning.getValue()).negate()); + cmd.validateOutput(xcodeWarning.copy().negate()); } }).execute(1); - }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); + }, MacSign.Keychain.UsageBuilder::addToSearchList, keychain); } @Test - public static void testCodesignUnspecifiedFailure() throws IOException { - - var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime(); - - appImageCmd.executeIgnoreExitCode().assertExitCodeIsZero(); + public static void testCodesignUnspecificFailure() throws IOException { // This test expects jpackage to respond in a specific way on a codesign failure. - // The simplest option to trigger codesign failure is to request the signing of an invalid bundle. - // Create app content directory with the name known to fail signing. - final var appContent = appImageCmd.appLayout().contentDirectory().resolve("foo.1"); - Files.createDirectory(appContent); - Files.createFile(appContent.resolve("file")); + // There are a few ways to make jpackage fail signing. One is using an erroneous + // combination of a signing key and a keychain. - final List expectedStrings = new ArrayList<>(); - expectedStrings.add(JPackageStringBundle.MAIN.cannedFormattedString("error.tool.failed.with.output", "codesign")); + var signingKeyOption = new SignKeyOption( + SIGN_KEY_IDENTITY, + SigningBase.StandardCertificateRequest.CODESIGN_ACME_TECH_LTD.certRequest( + SigningBase.StandardKeychain.MAIN.keychain())); MacSign.withKeychain(keychain -> { - var signingKeyOption = new SignKeyOptionWithKeychain( - SIGN_KEY_IDENTITY, - SigningBase.StandardCertificateRequest.CODESIGN, - keychain); + // Build a matcher for jpackage's failed command output. + var errorValidator = new FailedCommandErrorValidator(Pattern.compile(String.format( + "/usr/bin/codesign -s %s -vvvv --timestamp --options runtime --prefix \\S+ --keychain %s", + Pattern.quote(String.format("'%s'", signingKeyOption.certRequest().name())), + Pattern.quote(keychain.name()) + ))).exitCode(1).output(String.format("%s: no identity found", signingKeyOption.certRequest().name())).createGroup(); - new JPackageCommand().setPackageType(PackageType.IMAGE) + JPackageCommand.helloAppImage() + .setFakeRuntime() .ignoreDefaultVerbose(true) - .validateOutput(expectedStrings.toArray(CannedFormattedString[]::new)) - .addArguments("--app-image", appImageCmd.outputBundle()) + .validateOutput(errorValidator.create()) .mutate(signingKeyOption::addTo) + .mutate(MacHelper.useKeychain(keychain)) .execute(1); - }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.MAIN.keychain()); + }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.DUPLICATE.keychain()); } @Test From 9cd25d517c25477be6643bfb795843ca080d4e38 Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Sat, 7 Feb 2026 11:18:01 +0000 Subject: [PATCH 165/215] 8377359: TestOpaqueConstantBoolNodes fails on PPC64 Reviewed-by: dbriemann, chagedorn --- .../compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java index 66f3da23abe..b8db97f7ecb 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestOpaqueConstantBoolNodes.java @@ -40,6 +40,7 @@ public class TestOpaqueConstantBoolNodes { public static void main(String[] args) { TestFramework.runWithFlags( + "-XX:CompileCommand=inline,java.lang.String::*", "-XX:CompileCommand=inline,java.lang.StringCoding::*", "-XX:CompileCommand=exclude,jdk.internal.util.Preconditions::checkFromIndexSize"); } From 6665a78ee27617a5c9f272ee4471625a64636ac7 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Sat, 7 Feb 2026 18:20:55 +0000 Subject: [PATCH 166/215] 8376688: Gtest os.attempt_reserve_memory_between_small_range_fill_hole_vm fails on AIX 7.3 Reviewed-by: mdoerr, lucy --- test/hotspot/gtest/runtime/test_os_reserve_between.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/hotspot/gtest/runtime/test_os_reserve_between.cpp b/test/hotspot/gtest/runtime/test_os_reserve_between.cpp index 8e68be22749..7c256c7c3db 100644 --- a/test/hotspot/gtest/runtime/test_os_reserve_between.cpp +++ b/test/hotspot/gtest/runtime/test_os_reserve_between.cpp @@ -335,6 +335,8 @@ TEST_VM(os, attempt_reserve_memory_randomization_cornercases) { // Test that, regardless where the hole is in the [min, max) range, if we probe nonrandomly, we will fill that hole // as long as the range size is smaller than the number of probe attempts +// On AIX, the allocation granularity is too large and not well suited for 'small' holes, so we avoid the test +#if !defined(_AIX) TEST_VM(os, attempt_reserve_memory_between_small_range_fill_hole) { const size_t ps = os::vm_page_size(); const size_t ag = allocation_granularity(); @@ -348,3 +350,4 @@ TEST_VM(os, attempt_reserve_memory_between_small_range_fill_hole) { } } } +#endif From ffb6279c885e9d9a1a53ce7657390e286136c4b7 Mon Sep 17 00:00:00 2001 From: Chen Liang Date: Sun, 8 Feb 2026 01:29:48 +0000 Subject: [PATCH 167/215] 8377334: Test framework used by langtools regression tests can produce false positives Co-authored-by: Vicente Romero Reviewed-by: vromero --- .../combo/tools/javac/combo/JavacTemplateTestBase.java | 5 +++-- .../tools/javac/records/RecordCompilationTests.java | 8 ++++---- .../tools/javac/sealed/SealedCompilationTests.java | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java b/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java index 02c94b589c8..dd3f5639c21 100644 --- a/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java +++ b/test/langtools/lib/combo/tools/javac/combo/JavacTemplateTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, 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 @@ -246,7 +246,8 @@ public abstract class JavacTemplateTestBase { return destDir; } else { - ct.analyze(); + ct.call(); + // Failed result will show up in diags return nullDir; } } diff --git a/test/langtools/tools/javac/records/RecordCompilationTests.java b/test/langtools/tools/javac/records/RecordCompilationTests.java index 1de0ec48f0d..a8384ba4692 100644 --- a/test/langtools/tools/javac/records/RecordCompilationTests.java +++ b/test/langtools/tools/javac/records/RecordCompilationTests.java @@ -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 @@ -335,7 +335,7 @@ class RecordCompilationTests extends CompilationTestCase { assertFail("compiler.err.invalid.accessor.method.in.record", "public record R(int x) {\n" + - " static private final j = 0;" + + " static private final int j = 0;" + " static public int x() { return j; };" + "}"); } @@ -539,10 +539,10 @@ class RecordCompilationTests extends CompilationTestCase { "record R() { void test1() { class X { U u; } } }", "interface I { default void test1() { class X { void test2() { System.err.println(localVar); } } } }", - "interface I() { default void test1() { class X { void test2() {System.err.println(param);} } } }", + "interface I { default void test1() { class X { void test2() {System.err.println(param);} } } }", "interface I { default void test1() { class X { void test2() { System.err.println(instanceField); } } } }", "interface I { default void test1() { class X { T t; } } }", - "interface I() { default void test1() { class X {U u;} } }", + "interface I { default void test1() { class X {U u;} } }", "enum E { A; void test1() { class X { void test2() { System.err.println(localVar); } } } }", "enum E { A; void test1() { class X { void test2() {System.err.println(param);} } } }", diff --git a/test/langtools/tools/javac/sealed/SealedCompilationTests.java b/test/langtools/tools/javac/sealed/SealedCompilationTests.java index 17141ee3c67..b7207b777c6 100644 --- a/test/langtools/tools/javac/sealed/SealedCompilationTests.java +++ b/test/langtools/tools/javac/sealed/SealedCompilationTests.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 @@ -855,12 +855,13 @@ class SealedCompilationTests extends CompilationTestCase { @Test void testSealedNonSealedWithOtherModifiers() { + // Sup must be static so Sub may be static String template1 = """ @interface A {} class Outer { - sealed class Sup { } + static sealed class Sup { } # # class Sub extends Sup {} final class Sub2 extends Sub {} } From dc80ce7aec8e466a29fd4c94ee70c90a7244869f Mon Sep 17 00:00:00 2001 From: ikarostsin Date: Mon, 9 Feb 2026 07:41:20 +0000 Subject: [PATCH 168/215] 8374056: RISC-V: Fix argument passing for the RiscvFlushIcache::flush Reviewed-by: fyang, rehn --- src/hotspot/cpu/riscv/icache_riscv.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/riscv/icache_riscv.cpp b/src/hotspot/cpu/riscv/icache_riscv.cpp index 258bc665770..20de2dbb2ad 100644 --- a/src/hotspot/cpu/riscv/icache_riscv.cpp +++ b/src/hotspot/cpu/riscv/icache_riscv.cpp @@ -39,7 +39,8 @@ static int icache_flush(address addr, int lines, int magic) { // We need to make sure stores happens before the I/D cache synchronization. __asm__ volatile("fence rw, rw" : : : "memory"); - RiscvFlushIcache::flush((uintptr_t)addr, ((uintptr_t)lines) << ICache::log2_line_size); + uintptr_t end = (uintptr_t)addr + ((uintptr_t)lines << ICache::log2_line_size); + RiscvFlushIcache::flush((uintptr_t)addr, end); return magic; } From 1314857b335502998e22f114b401c29af2517548 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 9 Feb 2026 08:49:30 +0000 Subject: [PATCH 169/215] 8377443: G1: Remove unnecessary cast in ResizeTLABAndSwapCardTableTask Reviewed-by: ayang --- src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp index 46d12df575c..3f47d386015 100644 --- a/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGCPostEvacuateTasks.cpp @@ -854,7 +854,7 @@ public: void do_thread(Thread* thread) { if (UseTLAB && ResizeTLAB) { - static_cast(thread)->tlab().resize(); + thread->tlab().resize(); } G1BarrierSet::g1_barrier_set()->update_card_table_base(thread); From ee5b10c7f0ac5dba69d69fdd4b8a30b6443e6720 Mon Sep 17 00:00:00 2001 From: Ramkumar Sunderbabu Date: Mon, 9 Feb 2026 09:03:47 +0000 Subject: [PATCH 170/215] 8375443: AVX-512: Disabling through UseSHA doesn't affect UseSHA3Intrinsics Reviewed-by: mhaessig, kvn --- src/hotspot/cpu/x86/vm_version_x86.cpp | 14 +- ...nsicsWithUseSHADisabledOnSupportedCPU.java | 150 +++++++++++++++++ ...icsWithUseSHADisabledOnUnsupportedCPU.java | 153 ++++++++++++++++++ 3 files changed, 310 insertions(+), 7 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/arguments/TestUseSHA3IntrinsicsWithUseSHADisabledOnSupportedCPU.java create mode 100644 test/hotspot/jtreg/compiler/arguments/TestUseSHA3IntrinsicsWithUseSHADisabledOnUnsupportedCPU.java diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index eb401e4f877..c65c1c7d219 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -1359,16 +1359,16 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); } - if (supports_evex() && supports_avx512bw()) { - if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { - UseSHA3Intrinsics = true; - } + if (UseSHA && supports_evex() && supports_avx512bw()) { + if (FLAG_IS_DEFAULT(UseSHA3Intrinsics)) { + FLAG_SET_DEFAULT(UseSHA3Intrinsics, true); + } } else if (UseSHA3Intrinsics) { - warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); - FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); + warning("Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU."); + FLAG_SET_DEFAULT(UseSHA3Intrinsics, false); } - if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics)) { + if (!(UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics || UseSHA3Intrinsics)) { FLAG_SET_DEFAULT(UseSHA, false); } diff --git a/test/hotspot/jtreg/compiler/arguments/TestUseSHA3IntrinsicsWithUseSHADisabledOnSupportedCPU.java b/test/hotspot/jtreg/compiler/arguments/TestUseSHA3IntrinsicsWithUseSHADisabledOnSupportedCPU.java new file mode 100644 index 00000000000..2461f1ae92b --- /dev/null +++ b/test/hotspot/jtreg/compiler/arguments/TestUseSHA3IntrinsicsWithUseSHADisabledOnSupportedCPU.java @@ -0,0 +1,150 @@ +/* + * 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 8375443 + * @summary Verify that UseSHA3Intrinsics is properly disabled when UseSHA is disabled + * on supported CPU + * @library /test/lib / + * @requires vm.flagless + + * + * @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 + * compiler.arguments.TestUseSHA3IntrinsicsWithUseSHADisabledOnSupportedCPU + */ + +package compiler.arguments; + +import compiler.testlibrary.sha.predicate.IntrinsicPredicates; +import jdk.test.lib.cli.CommandLineOptionTest; +import jdk.test.lib.process.ExitCode; +import jtreg.SkippedException; + +public class TestUseSHA3IntrinsicsWithUseSHADisabledOnSupportedCPU { + private static final String OPTION_NAME = "UseSHA3Intrinsics"; + private static final String MASTER_OPTION = "UseSHA"; + private static final String WARNING_MESSAGE = + "Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU\\."; + private static final String UNLOCK_DIAGNOSTIC = "-XX:+UnlockDiagnosticVMOptions"; + + public static void main(String[] args) throws Throwable { + if (!IntrinsicPredicates.isSHA3IntrinsicAvailable().getAsBoolean()) { + throw new SkippedException("Skipping... SHA3 intrinsics are not available on this platform."); + } + + // Verify that UseSHA3Intrinsics can be explicitly enabled when UseSHA is enabled (default) + testExplicitEnableWithUseSHAEnabled(); + + // Verify that UseSHA3Intrinsics is forced to false when UseSHA is disabled, + // even if explicitly set to true + testForcedDisableWhenUseSHADisabled(); + + // Verify that a warning is printed when trying to enable UseSHA3Intrinsics + // while UseSHA is disabled + testWarningWhenEnablingWithUseSHADisabled(); + + // Verify that UseSHA3Intrinsics can be explicitly disabled even when UseSHA is enabled + testExplicitDisableWithUseSHAEnabled(); + } + + private static void testExplicitEnableWithUseSHAEnabled() throws Throwable { + // Verify the option value is true when explicitly enabled (with UseSHA enabled by default) + CommandLineOptionTest.verifyOptionValueForSameVM( + OPTION_NAME, + "true", + "UseSHA3Intrinsics should be enabled when explicitly set to true with UseSHA enabled", + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, true) + ); + + // Verify no warning is printed when enabling UseSHA3Intrinsics with UseSHA enabled + CommandLineOptionTest.verifySameJVMStartup( + null, // No specific output expected + new String[] { WARNING_MESSAGE }, // Warning should not appear + "No warning should be printed when enabling UseSHA3Intrinsics with UseSHA enabled", + "UseSHA3Intrinsics should be enabled without warnings when UseSHA is enabled", + ExitCode.OK, + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, true) + ); + } + + private static void testForcedDisableWhenUseSHADisabled() throws Throwable { + // When -XX:-UseSHA is set, UseSHA3Intrinsics should be forced to false + // even if +UseSHA3Intrinsics is explicitly passed + CommandLineOptionTest.verifyOptionValueForSameVM( + OPTION_NAME, + "false", + String.format("UseSHA3Intrinsics should be forced to false when %s is set, " + + "even if explicitly enabled", + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, false)), + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, true), + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, false) + ); + } + + private static void testWarningWhenEnablingWithUseSHADisabled() throws Throwable { + // A warning should be printed when trying to enable UseSHA3Intrinsics with -UseSHA + CommandLineOptionTest.verifySameJVMStartup( + new String[] { WARNING_MESSAGE }, // Warning should appear + null, // No unexpected output + "JVM should start successfully", + String.format("A warning should be printed when trying to enable %s while %s is disabled", + OPTION_NAME, + MASTER_OPTION), + ExitCode.OK, + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, false), + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, true) + ); + } + + private static void testExplicitDisableWithUseSHAEnabled() throws Throwable { + // Verify that UseSHA3Intrinsics can be explicitly disabled even when UseSHA is enabled + CommandLineOptionTest.verifyOptionValueForSameVM( + OPTION_NAME, + "false", + "UseSHA3Intrinsics should be disabled when explicitly set to false", + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, true), + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, false) + ); + + // Verify no warning is printed when explicitly disabling UseSHA3Intrinsics + CommandLineOptionTest.verifySameJVMStartup( + null, // No specific output expected + new String[] { WARNING_MESSAGE }, // Warning should not appear + "No warning should be printed when explicitly disabling UseSHA3Intrinsics", + "UseSHA3Intrinsics should be disabled without warnings", + ExitCode.OK, + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, true), + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, false) + ); + } +} diff --git a/test/hotspot/jtreg/compiler/arguments/TestUseSHA3IntrinsicsWithUseSHADisabledOnUnsupportedCPU.java b/test/hotspot/jtreg/compiler/arguments/TestUseSHA3IntrinsicsWithUseSHADisabledOnUnsupportedCPU.java new file mode 100644 index 00000000000..067bc723b5c --- /dev/null +++ b/test/hotspot/jtreg/compiler/arguments/TestUseSHA3IntrinsicsWithUseSHADisabledOnUnsupportedCPU.java @@ -0,0 +1,153 @@ +/* + * 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 8375443 + * @summary Verify that UseSHA3Intrinsics is properly disabled with warnings + * on unsupported CPU. + * @library /test/lib / + * @requires vm.flagless + * + * @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 + * compiler.arguments.TestUseSHA3IntrinsicsWithUseSHADisabledOnUnsupportedCPU + */ + +package compiler.arguments; + +import compiler.testlibrary.sha.predicate.IntrinsicPredicates; +import jdk.test.lib.cli.CommandLineOptionTest; +import jdk.test.lib.process.ExitCode; +import jtreg.SkippedException; + +public class TestUseSHA3IntrinsicsWithUseSHADisabledOnUnsupportedCPU { + private static final String OPTION_NAME = "UseSHA3Intrinsics"; + private static final String MASTER_OPTION = "UseSHA"; + private static final String WARNING_MESSAGE = + "Intrinsics for SHA3-224, SHA3-256, SHA3-384 and SHA3-512 crypto hash functions not available on this CPU\\."; + private static final String UNLOCK_DIAGNOSTIC = "-XX:+UnlockDiagnosticVMOptions"; + + public static void main(String[] args) throws Throwable { + if (IntrinsicPredicates.isSHA3IntrinsicAvailable().getAsBoolean()) { + throw new SkippedException("Skipping... SHA3 intrinsics are available on this platform."); + } + + // Verify that UseSHA3Intrinsics remains false when UseSHA is enabled + // but CPU doesn't support the instructions + testRemainsDisabledWithUseSHAEnabled(); + + // Verify that explicitly disabling UseSHA3Intrinsics works without warnings + testExplicitDisableWithoutWarning(); + + // Verify behavior with both -XX:-UseSHA and +XX:+UseSHA3Intrinsics + testWithUseSHADisabled(); + } + + private static void testRemainsDisabledWithUseSHAEnabled() throws Throwable { + // Even with +UseSHA, UseSHA3Intrinsics should remain false on unsupported CPU + CommandLineOptionTest.verifyOptionValueForSameVM( + OPTION_NAME, + "false", + "UseSHA3Intrinsics should remain false even when UseSHA is enabled on unsupported CPU", + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, true) + ); + + // Trying to enable both should still produce a warning + CommandLineOptionTest.verifySameJVMStartup( + new String[] { WARNING_MESSAGE }, + null, + "JVM should start with a warning", + "Warning should be printed even when UseSHA is explicitly enabled", + ExitCode.OK, + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, true), + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, true) + ); + } + + private static void testExplicitDisableWithoutWarning() throws Throwable { + // Explicitly disabling should not produce any warnings + CommandLineOptionTest.verifySameJVMStartup( + null, // No specific output expected + new String[] { WARNING_MESSAGE }, // Warning should NOT appear + "JVM should start without warnings", + "No warning should be printed when explicitly disabling UseSHA3Intrinsics", + ExitCode.OK, + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, false) + ); + + // Verify the flag value is false + CommandLineOptionTest.verifyOptionValueForSameVM( + OPTION_NAME, + "false", + "UseSHA3Intrinsics should be false when explicitly disabled", + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, false) + ); + } + + private static void testWithUseSHADisabled() throws Throwable { + // When UseSHA is disabled, UseSHA3Intrinsics should also be disabled + // and a warning should be printed + CommandLineOptionTest.verifySameJVMStartup( + new String[] { WARNING_MESSAGE }, + null, + "JVM should start with a warning", + String.format("Warning should be printed when trying to enable %s while %s is disabled on unsupported CPU", + OPTION_NAME, + MASTER_OPTION), + ExitCode.OK, + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, false), + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, true) + ); + + // Verify the flag is forced to false + CommandLineOptionTest.verifyOptionValueForSameVM( + OPTION_NAME, + "false", + String.format("UseSHA3Intrinsics should be false when %s is disabled on unsupported CPU", + MASTER_OPTION), + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, false), + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, true) + ); + + // Test that with both flags disabled, no warning appears + CommandLineOptionTest.verifySameJVMStartup( + null, + new String[] { WARNING_MESSAGE }, + "JVM should start without warnings", + "No warning when both UseSHA and UseSHA3Intrinsics are disabled", + ExitCode.OK, + UNLOCK_DIAGNOSTIC, + CommandLineOptionTest.prepareBooleanFlag(MASTER_OPTION, false), + CommandLineOptionTest.prepareBooleanFlag(OPTION_NAME, false) + ); + } +} From a7bf468a8fd86bf2845acef8aea1462f865c8182 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 9 Feb 2026 09:44:28 +0000 Subject: [PATCH 171/215] 8377165: G1: Introduce common G1 GC Mark to collect scoped objects Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 38 +++++++++++++-------- src/hotspot/share/gc/g1/g1FullCollector.cpp | 2 +- src/hotspot/share/gc/g1/g1FullCollector.hpp | 14 +------- src/hotspot/share/gc/g1/g1FullGCScope.cpp | 7 ++-- src/hotspot/share/gc/g1/g1FullGCScope.hpp | 9 ++--- 5 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 9424a804bd8..8fc2c7c9941 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -138,6 +138,26 @@ void G1RegionMappingChangedListener::on_commit(uint start_idx, size_t num_region reset_from_card_cache(start_idx, num_regions); } +// Collects commonly used scoped objects that are related to initial setup. +class G1GCMark : StackObj { + ResourceMark _rm; + IsSTWGCActiveMark _active_gc_mark; + GCIdMark _gc_id_mark; + SvcGCMarker _sgcm; + GCTraceCPUTime _tcpu; + +public: + G1GCMark(GCTracer* tracer, bool is_full_gc) : + _rm(), + _active_gc_mark(), + _gc_id_mark(), + _sgcm(is_full_gc ? SvcGCMarker::FULL : SvcGCMarker::MINOR), + _tcpu(tracer) { + + assert_at_safepoint_on_vm_thread(); + } +}; + void G1CollectedHeap::run_batch_task(G1BatchedTask* cl) { uint num_workers = MAX2(1u, MIN2(cl->num_workers_estimate(), workers()->active_workers())); cl->set_max_workers(num_workers); @@ -914,12 +934,11 @@ void G1CollectedHeap::verify_after_full_collection() { void G1CollectedHeap::do_full_collection(size_t allocation_word_size, bool clear_all_soft_refs, bool do_maximal_compaction) { - assert_at_safepoint_on_vm_thread(); - - G1FullGCMark gc_mark; + G1FullGCTracer tracer; + G1GCMark gc_mark(&tracer, true /* is_full_gc */); GCTraceTime(Info, gc) tm("Pause Full", nullptr, gc_cause(), true); - G1FullCollector collector(this, clear_all_soft_refs, do_maximal_compaction, gc_mark.tracer()); + G1FullCollector collector(this, clear_all_soft_refs, do_maximal_compaction, &tracer); collector.prepare_collection(); collector.collect(); collector.complete_collection(allocation_word_size); @@ -2714,16 +2733,7 @@ void G1CollectedHeap::flush_region_pin_cache() { } void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_size) { - assert_at_safepoint_on_vm_thread(); - assert(!is_stw_gc_active(), "collection is not reentrant"); - - ResourceMark rm; - - IsSTWGCActiveMark active_gc_mark; - GCIdMark gc_id_mark; - SvcGCMarker sgcm(SvcGCMarker::MINOR); - - GCTraceCPUTime tcpu(_gc_tracer_stw); + G1GCMark gcm(_gc_tracer_stw, false /* is_full_gc */); _bytes_used_during_gc = 0; diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 06db5f612a1..b6388c2f722 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -110,7 +110,7 @@ uint G1FullCollector::calc_active_workers() { G1FullCollector::G1FullCollector(G1CollectedHeap* heap, bool clear_soft_refs, bool do_maximal_compaction, - G1FullGCTracer* tracer) : + GCTracer* tracer) : _heap(heap), _scope(heap->monitoring_support(), clear_soft_refs, do_maximal_compaction, tracer), _num_workers(calc_active_workers()), diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index 7e455b07013..605556a2ba6 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp @@ -58,18 +58,6 @@ public: } }; -// Full GC Mark that holds GC id and CPU time trace. Needs to be separate -// from the G1FullCollector and G1FullGCScope to allow the Full GC logging -// to have the same structure as the Young GC logging. -class G1FullGCMark : StackObj { - GCIdMark _gc_id; - G1FullGCTracer _tracer; - GCTraceCPUTime _cpu_time; -public: - G1FullGCMark() : _gc_id(), _tracer(), _cpu_time(&_tracer) { } - G1FullGCTracer* tracer() { return &_tracer; } -}; - // The G1FullCollector holds data associated with the current Full GC. class G1FullCollector : StackObj { G1CollectedHeap* _heap; @@ -102,7 +90,7 @@ public: G1FullCollector(G1CollectedHeap* heap, bool clear_soft_refs, bool do_maximal_compaction, - G1FullGCTracer* tracer); + GCTracer* tracer); ~G1FullCollector(); void prepare_collection(); diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.cpp b/src/hotspot/share/gc/g1/g1FullGCScope.cpp index 083b77b44b7..cb4ebe423ff 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.cpp @@ -38,14 +38,11 @@ G1FullGCJFRTracerMark::~G1FullGCJFRTracerMark() { G1FullGCScope::G1FullGCScope(G1MonitoringSupport* monitoring_support, bool clear_soft, bool do_maximal_compaction, - G1FullGCTracer* tracer) : - _rm(), + GCTracer* tracer) : _should_clear_soft_refs(clear_soft), _do_maximal_compaction(do_maximal_compaction), - _svc_marker(SvcGCMarker::FULL), _timer(), _tracer(tracer), - _active(), _tracer_mark(&_timer, _tracer), _monitoring_scope(monitoring_support), _heap_printer(G1CollectedHeap::heap()), @@ -57,7 +54,7 @@ STWGCTimer* G1FullGCScope::timer() { return &_timer; } -G1FullGCTracer* G1FullGCScope::tracer() { +GCTracer* G1FullGCScope::tracer() { return _tracer; } diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.hpp b/src/hotspot/share/gc/g1/g1FullGCScope.hpp index 278a00cedbd..fc9d5a71f92 100644 --- a/src/hotspot/share/gc/g1/g1FullGCScope.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCScope.hpp @@ -46,13 +46,10 @@ public: // Class used to group scoped objects used in the Full GC together. class G1FullGCScope : public StackObj { - ResourceMark _rm; bool _should_clear_soft_refs; bool _do_maximal_compaction; - SvcGCMarker _svc_marker; STWGCTimer _timer; - G1FullGCTracer* _tracer; - IsSTWGCActiveMark _active; + GCTracer* _tracer; G1FullGCJFRTracerMark _tracer_mark; G1FullGCMonitoringScope _monitoring_scope; G1HeapPrinterMark _heap_printer; @@ -62,13 +59,13 @@ public: G1FullGCScope(G1MonitoringSupport* monitoring_support, bool clear_soft, bool do_maximal_compaction, - G1FullGCTracer* tracer); + GCTracer* tracer); bool should_clear_soft_refs() const { return _should_clear_soft_refs; } bool do_maximal_compaction() { return _do_maximal_compaction; } STWGCTimer* timer(); - G1FullGCTracer* tracer(); + GCTracer* tracer(); size_t region_compaction_threshold() const; }; From 07f78779e099d2dead74a05acf84ac4c457293b5 Mon Sep 17 00:00:00 2001 From: Guanqiang Han Date: Mon, 9 Feb 2026 09:47:45 +0000 Subject: [PATCH 172/215] 8376491: ZGC: crash in __memset_evex_unaligned_erms when initializing heap using high values for -XX:ConcGCThreads Reviewed-by: aboldtch, stefank --- src/hotspot/share/gc/z/zUtils.inline.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/z/zUtils.inline.hpp b/src/hotspot/share/gc/z/zUtils.inline.hpp index b83f42d18e6..a221a925498 100644 --- a/src/hotspot/share/gc/z/zUtils.inline.hpp +++ b/src/hotspot/share/gc/z/zUtils.inline.hpp @@ -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 @@ -37,6 +37,9 @@ inline uintptr_t ZUtils::alloc_aligned_unfreeable(size_t alignment, size_t size) { const size_t padded_size = size + (alignment - 1); void* const addr = os::malloc(padded_size, mtGC); + if (addr == nullptr) { + vm_exit_out_of_memory(padded_size, OOM_MALLOC_ERROR, "ZGC alloc_aligned_unfreeable malloc failed"); + } void* const aligned_addr = align_up(addr, alignment); memset(aligned_addr, 0, size); From b12367e19695a15a83ba85b58b4c22dce69d7075 Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Mon, 9 Feb 2026 10:32:53 +0000 Subject: [PATCH 173/215] 8365381: [asan] exclude tests under ASAN build which rely on vm signal handling Reviewed-by: dholmes, syan --- .../AccessZeroNKlassHitsProtectionZone.java | 10 +++++++++- .../ErrorHandling/MachCodeFramesInErrorFile.java | 4 +++- .../jtreg/runtime/ErrorHandling/ResourceMarkTest.java | 4 +++- .../runtime/ErrorHandling/SecondaryErrorTest.java | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java index a9d957bac18..61d017d2264 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/AccessZeroNKlassHitsProtectionZone.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2025, Red Hat, Inc. - * 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,6 +28,8 @@ * @library /test/lib * @requires vm.bits == 64 & vm.debug == true & vm.flagless * @requires os.family != "aix" + * @comment This test relies on crashing which conflicts with ASAN checks + * @requires !vm.asan * @modules java.base/jdk.internal.misc * java.management * @build jdk.test.whitebox.WhiteBox @@ -40,6 +42,8 @@ * @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 os.family != "aix" + * @comment This test relies on crashing which conflicts with ASAN checks + * @requires !vm.asan * @library /test/lib * @modules java.base/jdk.internal.misc * java.management @@ -53,6 +57,8 @@ * @summary Test that dereferencing a Klass that is the result of a decode(0) crashes accessing the nKlass guard zone * @requires vm.bits == 64 & vm.debug == true & vm.flagless * @requires os.family != "aix" + * @comment This test relies on crashing which conflicts with ASAN checks + * @requires !vm.asan * @library /test/lib * @modules java.base/jdk.internal.misc * java.management @@ -66,6 +72,8 @@ * @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 os.family != "aix" + * @comment This test relies on crashing which conflicts with ASAN checks + * @requires !vm.asan * @library /test/lib * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java b/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java index 74cedae5f1a..ba36d5810d8 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/MachCodeFramesInErrorFile.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 @@ -27,6 +27,8 @@ * @requires vm.flagless * @requires vm.compiler2.enabled * @requires test.thread.factory == null + * @comment This test relies on crashing which conflicts with ASAN checks + * @requires !vm.asan * @summary Test that abstract machine code is dumped for the top frames in a hs-err log * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ResourceMarkTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/ResourceMarkTest.java index 5bbc4cfac00..583ed894035 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/ResourceMarkTest.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/ResourceMarkTest.java @@ -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. * 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,8 @@ * @library /test/lib * @requires vm.flagless * @requires vm.debug + * @comment ASAN grabs SIGFPE earlier than vm signal handler + * @requires !vm.asan * @requires os.family != "windows" * @modules java.base/jdk.internal.misc * java.management diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/SecondaryErrorTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/SecondaryErrorTest.java index 5b28e2a1d8b..f7facaab186 100644 --- a/test/hotspot/jtreg/runtime/ErrorHandling/SecondaryErrorTest.java +++ b/test/hotspot/jtreg/runtime/ErrorHandling/SecondaryErrorTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2014, 2024 SAP SE. All rights reserved. - * Copyright (c) 2014, 2024, 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 @@ -29,6 +29,8 @@ * @library /test/lib * @requires vm.flagless * @requires vm.debug + * @comment ASAN grabs SIGFPE earlier than vm signal handler + * @requires !vm.asan * @requires os.family != "windows" * @modules java.base/jdk.internal.misc * java.management @@ -41,6 +43,8 @@ * @library /test/lib * @requires vm.flagless * @requires vm.debug + * @comment ASAN grabs SIGFPE earlier than vm signal handler + * @requires !vm.asan * @requires os.family != "windows" * @modules java.base/jdk.internal.misc * java.management From d10ddb820316a053c58a61ba706af7548d089acf Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 9 Feb 2026 10:52:09 +0000 Subject: [PATCH 174/215] 8377352: Parallel: Incorrect capacity in GC overhead log Reviewed-by: tschatzl, kbarrett --- src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 0d8a3166f79..0f55de90a8a 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -438,7 +438,7 @@ bool ParallelScavengeHeap::check_gc_overhead_limit() { log_debug(gc)("GC Overhead Limit: GC Time %f Free Space Young %f Old %f Counter %zu", (100 - _size_policy->mutator_time_percent()), percent_of(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes()), - percent_of(_old_gen->free_in_bytes(), _young_gen->capacity_in_bytes()), + percent_of(_old_gen->free_in_bytes(), _old_gen->capacity_in_bytes()), _gc_overhead_counter); if (little_mutator_time && little_free_space) { From a5765a916a03471cd771c870e1c0e5eab7a08bf1 Mon Sep 17 00:00:00 2001 From: Anjian Wen Date: Mon, 9 Feb 2026 11:50:40 +0000 Subject: [PATCH 175/215] 8377225: RISC-V: Improve receiver type profiling reliability Reviewed-by: shade, fjiang, fyang --- .../cpu/riscv/c1_LIRAssembler_riscv.cpp | 68 ++------ .../cpu/riscv/c1_LIRAssembler_riscv.hpp | 5 +- src/hotspot/cpu/riscv/interp_masm_riscv.cpp | 159 +----------------- src/hotspot/cpu/riscv/interp_masm_riscv.hpp | 11 +- .../cpu/riscv/macroAssembler_riscv.cpp | 154 +++++++++++++++++ .../cpu/riscv/macroAssembler_riscv.hpp | 2 + src/hotspot/cpu/riscv/templateTable_riscv.cpp | 4 +- 7 files changed, 176 insertions(+), 227 deletions(-) diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index e77a2067e89..63e2fd015d7 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -1041,31 +1041,10 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) { __ bind(*op->stub()->continuation()); } -void LIR_Assembler::type_profile_helper(Register mdo, ciMethodData *md, ciProfileData *data, - Register recv, Label* update_done) { - for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) { - Label next_test; - // See if the receiver is receiver[n]. - __ ld(t1, Address(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)))); - __ bne(recv, t1, next_test); - Address data_addr(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i))); - __ increment(data_addr, DataLayout::counter_increment); - __ j(*update_done); - __ bind(next_test); - } - - // Didn't find receiver; find next empty slot and fill it in - for (uint i = 0; i < ReceiverTypeData::row_limit(); i++) { - Label next_test; - Address recv_addr(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i))); - __ ld(t1, recv_addr); - __ bnez(t1, next_test); - __ sd(recv, recv_addr); - __ mv(t1, DataLayout::counter_increment); - __ sd(t1, Address(mdo, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i)))); - __ j(*update_done); - __ bind(next_test); - } +void LIR_Assembler::type_profile_helper(Register mdo, ciMethodData *md, + ciProfileData *data, Register recv) { + int mdp_offset = md->byte_offset_of_slot(data, in_ByteSize(0)); + __ profile_receiver_type(recv, mdo, mdp_offset); } void LIR_Assembler::data_check(LIR_OpTypeCheck *op, ciMethodData **md, ciProfileData **data) { @@ -1139,14 +1118,9 @@ void LIR_Assembler::profile_object(ciMethodData* md, ciProfileData* data, Regist __ j(*obj_is_null); __ bind(not_null); - Label update_done; Register recv = k_RInfo; __ load_klass(recv, obj); - type_profile_helper(mdo, md, data, recv, &update_done); - Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); - __ increment(counter_addr, DataLayout::counter_increment); - - __ bind(update_done); + type_profile_helper(mdo, md, data, recv); } void LIR_Assembler::typecheck_loaded(LIR_OpTypeCheck *op, ciKlass* k, Register k_RInfo) { @@ -1554,11 +1528,8 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { // We know the type that will be seen at this call site; we can // statically update the MethodData* rather than needing to do // dynamic tests on the receiver type - // NOTE: we should probably put a lock around this search to - // avoid collisions by concurrent compilations ciVirtualCallData* vc_data = (ciVirtualCallData*) data; - uint i; - for (i = 0; i < VirtualCallData::row_limit(); i++) { + for (uint i = 0; i < VirtualCallData::row_limit(); i++) { ciKlass* receiver = vc_data->receiver(i); if (known_klass->equals(receiver)) { Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); @@ -1566,32 +1537,13 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) { return; } } - - // Receiver type not found in profile data; select an empty slot - // Note that this is less efficient than it should be because it - // always does a write to the receiver part of the - // VirtualCallData rather than just the first time - for (i = 0; i < VirtualCallData::row_limit(); i++) { - ciKlass* receiver = vc_data->receiver(i); - if (receiver == nullptr) { - Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i))); - __ mov_metadata(t1, known_klass->constant_encoding()); - __ sd(t1, recv_addr); - Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))); - __ increment(data_addr, DataLayout::counter_increment); - return; - } - } + // Receiver type is not found in profile data. + // Fall back to runtime helper to handle the rest at runtime. + __ mov_metadata(recv, known_klass->constant_encoding()); } else { __ load_klass(recv, recv); - Label update_done; - type_profile_helper(mdo, md, data, recv, &update_done); - // Receiver did not match any saved receiver and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - __ increment(counter_addr, DataLayout::counter_increment); - - __ bind(update_done); } + type_profile_helper(mdo, md, data, recv); } else { // Static call __ increment(counter_addr, DataLayout::counter_increment); diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp index 1e466e90d37..90b6b3ee4f4 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp @@ -54,9 +54,8 @@ private: Address stack_slot_address(int index, uint shift, int adjust = 0); // Record the type of the receiver in ReceiverTypeData - void type_profile_helper(Register mdo, - ciMethodData *md, ciProfileData *data, - Register recv, Label* update_done); + void type_profile_helper(Register mdo, ciMethodData *md, + ciProfileData *data, Register recv); void casw(Register addr, Register newval, Register cmpval); void caswu(Register addr, Register newval, Register cmpval); diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp index 189c7c93d07..744590bec2b 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp @@ -237,15 +237,14 @@ void InterpreterMacroAssembler::load_resolved_klass_at_offset( // Rsub_klass: subklass // // Kills: -// x12, x15 +// x12 void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, Label& ok_is_subtype) { assert(Rsub_klass != x10, "x10 holds superklass"); assert(Rsub_klass != x12, "x12 holds 2ndary super array length"); - assert(Rsub_klass != x15, "x15 holds 2ndary super array scan ptr"); // Profile the not-null value's klass. - profile_typecheck(x12, Rsub_klass, x15); // blows x12, reloads x15 + profile_typecheck(x12, Rsub_klass); // blows x12 // Do the check. check_klass_subtype(Rsub_klass, x10, x12, ok_is_subtype); // blows x12 @@ -1042,7 +1041,6 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) { void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register mdp, - Register reg2, bool receiver_can_be_null) { if (ProfileInterpreter) { Label profile_continue; @@ -1060,7 +1058,7 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, } // Record the receiver type. - record_klass_in_profile(receiver, mdp, reg2); + profile_receiver_type(receiver, mdp, 0); bind(skip_receiver_profile); // The method data pointer needs to be updated to reflect the new target. @@ -1072,153 +1070,6 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, } } -// This routine creates a state machine for updating the multi-row -// type profile at a virtual call site (or other type-sensitive bytecode). -// The machine visits each row (of receiver/count) until the receiver type -// is found, or until it runs out of rows. At the same time, it remembers -// the location of the first empty row. (An empty row records null for its -// receiver, and can be allocated for a newly-observed receiver type.) -// Because there are two degrees of freedom in the state, a simple linear -// search will not work; it must be a decision tree. Hence this helper -// function is recursive, to generate the required tree structured code. -// It's the interpreter, so we are trading off code space for speed. -// See below for example code. -void InterpreterMacroAssembler::record_klass_in_profile_helper( - Register receiver, Register mdp, - Register reg2, Label& done) { - if (TypeProfileWidth == 0) { - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - } else { - record_item_in_profile_helper(receiver, mdp, reg2, 0, done, TypeProfileWidth, - &VirtualCallData::receiver_offset, &VirtualCallData::receiver_count_offset); - } -} - -void InterpreterMacroAssembler::record_item_in_profile_helper(Register item, Register mdp, - Register reg2, int start_row, Label& done, int total_rows, - OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn) { - int last_row = total_rows - 1; - assert(start_row <= last_row, "must be work left to do"); - // Test this row for both the item and for null. - // Take any of three different outcomes: - // 1. found item => increment count and goto done - // 2. found null => keep looking for case 1, maybe allocate this cell - // 3. found something else => keep looking for cases 1 and 2 - // Case 3 is handled by a recursive call. - for (int row = start_row; row <= last_row; row++) { - Label next_test; - bool test_for_null_also = (row == start_row); - - // See if the item is item[n]. - int item_offset = in_bytes(item_offset_fn(row)); - test_mdp_data_at(mdp, item_offset, item, - (test_for_null_also ? reg2 : noreg), - next_test); - // (Reg2 now contains the item from the CallData.) - - // The item is item[n]. Increment count[n]. - int count_offset = in_bytes(item_count_offset_fn(row)); - increment_mdp_data_at(mdp, count_offset); - j(done); - bind(next_test); - - if (test_for_null_also) { - Label found_null; - // Failed the equality check on item[n]... Test for null. - if (start_row == last_row) { - // The only thing left to do is handle the null case. - beqz(reg2, found_null); - // Item did not match any saved item and there is no empty row for it. - // Increment total counter to indicate polymorphic case. - increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset())); - j(done); - bind(found_null); - break; - } - // Since null is rare, make it be the branch-taken case. - beqz(reg2, found_null); - - // Put all the "Case 3" tests here. - record_item_in_profile_helper(item, mdp, reg2, start_row + 1, done, total_rows, - item_offset_fn, item_count_offset_fn); - - // Found a null. Keep searching for a matching item, - // but remember that this is an empty (unused) slot. - bind(found_null); - } - } - - // In the fall-through case, we found no matching item, but we - // observed the item[start_row] is null. - // Fill in the item field and increment the count. - int item_offset = in_bytes(item_offset_fn(start_row)); - set_mdp_data_at(mdp, item_offset, item); - int count_offset = in_bytes(item_count_offset_fn(start_row)); - mv(reg2, DataLayout::counter_increment); - set_mdp_data_at(mdp, count_offset, reg2); - if (start_row > 0) { - j(done); - } -} - -// Example state machine code for three profile rows: -// # main copy of decision tree, rooted at row[1] -// if (row[0].rec == rec) then [ -// row[0].incr() -// goto done -// ] -// if (row[0].rec != nullptr) then [ -// # inner copy of decision tree, rooted at row[1] -// if (row[1].rec == rec) then [ -// row[1].incr() -// goto done -// ] -// if (row[1].rec != nullptr) then [ -// # degenerate decision tree, rooted at row[2] -// if (row[2].rec == rec) then [ -// row[2].incr() -// goto done -// ] -// if (row[2].rec != nullptr) then [ -// count.incr() -// goto done -// ] # overflow -// row[2].init(rec) -// goto done -// ] else [ -// # remember row[1] is empty -// if (row[2].rec == rec) then [ -// row[2].incr() -// goto done -// ] -// row[1].init(rec) -// goto done -// ] -// else [ -// # remember row[0] is empty -// if (row[1].rec == rec) then [ -// row[1].incr() -// goto done -// ] -// if (row[2].rec == rec) then [ -// row[2].incr() -// goto done -// ] -// row[0].init(rec) -// goto done -// ] -// done: - -void InterpreterMacroAssembler::record_klass_in_profile(Register receiver, - Register mdp, Register reg2) { - assert(ProfileInterpreter, "must be profiling"); - Label done; - - record_klass_in_profile_helper(receiver, mdp, reg2, done); - - bind(done); -} - void InterpreterMacroAssembler::profile_ret(Register return_bci, Register mdp) { if (ProfileInterpreter) { Label profile_continue; @@ -1274,7 +1125,7 @@ void InterpreterMacroAssembler::profile_null_seen(Register mdp) { } } -void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, Register reg2) { +void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass) { if (ProfileInterpreter) { Label profile_continue; @@ -1287,7 +1138,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass, mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size()); // Record the object type. - record_klass_in_profile(klass, mdp, reg2); + profile_receiver_type(klass, mdp, 0); } update_mdp_by_constant(mdp, mdp_delta); diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp index a9df09d656a..59cc76b022f 100644 --- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp +++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp @@ -262,14 +262,6 @@ class InterpreterMacroAssembler: public MacroAssembler { Register test_value_out, Label& not_equal_continue); - void record_klass_in_profile(Register receiver, Register mdp, - Register reg2); - void record_klass_in_profile_helper(Register receiver, Register mdp, - Register reg2, Label& done); - void record_item_in_profile_helper(Register item, Register mdp, - Register reg2, int start_row, Label& done, int total_rows, - OffsetFunction item_offset_fn, OffsetFunction item_count_offset_fn); - void update_mdp_by_offset(Register mdp_in, int offset_of_offset); void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp); void update_mdp_by_constant(Register mdp_in, int constant); @@ -283,11 +275,10 @@ class InterpreterMacroAssembler: public MacroAssembler { void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, - Register t1, bool receiver_can_be_null = false); void profile_ret(Register return_bci, Register mdp); void profile_null_seen(Register mdp); - void profile_typecheck(Register mdp, Register klass, Register temp); + void profile_typecheck(Register mdp, Register klass); void profile_typecheck_failed(Register mdp); void profile_switch_default(Register mdp); void profile_switch_case(Register index_in_scratch, Register mdp, diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index fb30f64e9ed..4f5e7afc166 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -543,6 +543,160 @@ void MacroAssembler::_verify_oop(Register reg, const char* s, const char* file, BLOCK_COMMENT("} verify_oop"); } +// Handle the receiver type profile update given the "recv" klass. +// +// Normally updates the ReceiverData (RD) that starts at "mdp" + "mdp_offset". +// If there are no matching or claimable receiver entries in RD, updates +// the polymorphic counter. +// +// This code expected to run by either the interpreter or JIT-ed code, without +// extra synchronization. For safety, receiver cells are claimed atomically, which +// avoids grossly misrepresenting the profiles under concurrent updates. For speed, +// counter updates are not atomic. +// +void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_offset) { + assert_different_registers(recv, mdp, t0, t1); + + int base_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(0)); + int end_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(ReceiverTypeData::row_limit())); + int poly_count_offset = in_bytes(CounterData::count_offset()); + int receiver_step = in_bytes(ReceiverTypeData::receiver_offset(1)) - base_receiver_offset; + int receiver_to_count_step = in_bytes(ReceiverTypeData::receiver_count_offset(0)) - base_receiver_offset; + + // Adjust for MDP offsets. Slots are pointer-sized, so is the global offset. + base_receiver_offset += mdp_offset; + end_receiver_offset += mdp_offset; + poly_count_offset += mdp_offset; + +#ifdef ASSERT + // We are about to walk the MDO slots without asking for offsets. + // Check that our math hits all the right spots. + for (uint c = 0; c < ReceiverTypeData::row_limit(); c++) { + int real_recv_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_offset(c)); + int real_count_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_count_offset(c)); + int offset = base_receiver_offset + receiver_step*c; + int count_offset = offset + receiver_to_count_step; + assert(offset == real_recv_offset, "receiver slot math"); + assert(count_offset == real_count_offset, "receiver count math"); + } + int real_poly_count_offset = mdp_offset + in_bytes(CounterData::count_offset()); + assert(poly_count_offset == real_poly_count_offset, "poly counter math"); +#endif + + // Corner case: no profile table. Increment poly counter and exit. + if (ReceiverTypeData::row_limit() == 0) { + increment(Address(mdp, poly_count_offset), DataLayout::counter_increment); + return; + } + + Register offset = t1; + + Label L_loop_search_receiver, L_loop_search_empty; + Label L_restart, L_found_recv, L_found_empty, L_polymorphic, L_count_update; + + // The code here recognizes three major cases: + // A. Fastest: receiver found in the table + // B. Fast: no receiver in the table, and the table is full + // C. Slow: no receiver in the table, free slots in the table + // + // The case A performance is most important, as perfectly-behaved code would end up + // there, especially with larger TypeProfileWidth. The case B performance is + // important as well, this is where bulk of code would land for normally megamorphic + // cases. The case C performance is not essential, its job is to deal with installation + // races, we optimize for code density instead. Case C needs to make sure that receiver + // rows are only claimed once. This makes sure we never overwrite a row for another + // receiver and never duplicate the receivers in the list, making profile type-accurate. + // + // It is very tempting to handle these cases in a single loop, and claim the first slot + // without checking the rest of the table. But, profiling code should tolerate free slots + // in the table, as class unloading can clear them. After such cleanup, the receiver + // we need might be _after_ the free slot. Therefore, we need to let at least full scan + // to complete, before trying to install new slots. Splitting the code in several tight + // loops also helpfully optimizes for cases A and B. + // + // This code is effectively: + // + // restart: + // // Fastest: receiver is already installed + // for (i = 0; i < receiver_count(); i++) { + // if (receiver(i) == recv) goto found_recv(i); + // } + // + // // Fast: no receiver, but profile is full + // for (i = 0; i < receiver_count(); i++) { + // if (receiver(i) == null) goto found_null(i); + // } + // goto polymorphic + // + // // Slow: try to install receiver + // found_null(i): + // CAS(&receiver(i), null, recv); + // goto restart + // + // polymorphic: + // count++; + // return + // + // found_recv(i): + // *receiver_count(i)++ + // + + bind(L_restart); + + // Fastest: receiver is already installed + mv(offset, base_receiver_offset); + bind(L_loop_search_receiver); + add(t0, mdp, offset); + ld(t0, Address(t0)); + beq(recv, t0, L_found_recv); + add(offset, offset, receiver_step); + sub(t0, offset, end_receiver_offset); + bnez(t0, L_loop_search_receiver); + + // Fast: no receiver, but profile is full + mv(offset, base_receiver_offset); + bind(L_loop_search_empty); + add(t0, mdp, offset); + ld(t0, Address(t0)); + beqz(t0, L_found_empty); + add(offset, offset, receiver_step); + sub(t0, offset, end_receiver_offset); + bnez(t0, L_loop_search_empty); + j(L_polymorphic); + + // Slow: try to install receiver + bind(L_found_empty); + + // Atomically swing receiver slot: null -> recv. + // + // The update uses CAS, which clobbers t0. Therefore, t1 + // is used to hold the destination address. This is safe because the + // offset is no longer needed after the address is computed. + add(t1, mdp, offset); + weak_cmpxchg(/*addr*/ t1, /*expected*/ zr, /*new*/ recv, Assembler::int64, + /*acquire*/ Assembler::relaxed, /*release*/ Assembler::relaxed, /*result*/ t0); + + // CAS success means the slot now has the receiver we want. CAS failure means + // something had claimed the slot concurrently: it can be the same receiver we want, + // or something else. Since this is a slow path, we can optimize for code density, + // and just restart the search from the beginning. + j(L_restart); + + // Counter updates: + // Increment polymorphic counter instead of receiver slot. + bind(L_polymorphic); + mv(offset, poly_count_offset); + j(L_count_update); + + // Found a receiver, convert its slot offset to corresponding count offset. + bind(L_found_recv); + add(offset, offset, receiver_to_count_step); + + bind(L_count_update); + add(t1, mdp, offset); + increment(Address(t1), DataLayout::counter_increment); +} + void MacroAssembler::_verify_oop_addr(Address addr, const char* s, const char* file, int line) { if (!VerifyOops) { return; diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 3b021388fa5..f5e985c28a2 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -390,6 +390,8 @@ class MacroAssembler: public Assembler { Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0); + void profile_receiver_type(Register recv, Register mdp, int mdp_offset); + // only if +VerifyOops void _verify_oop(Register reg, const char* s, const char* file, int line); void _verify_oop_addr(Address addr, const char* s, const char* file, int line); diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 0fb529d1683..5cc725e3af4 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -3279,7 +3279,7 @@ void TemplateTable::invokevirtual_helper(Register index, __ load_klass(x10, recv); // profile this call - __ profile_virtual_call(x10, xlocals, x13); + __ profile_virtual_call(x10, xlocals); // get target Method & entry point __ lookup_virtual_method(x10, index, method); @@ -3406,7 +3406,7 @@ void TemplateTable::invokeinterface(int byte_no) { /*return_method=*/false); // profile this call - __ profile_virtual_call(x13, x30, x9); + __ profile_virtual_call(x13, x30); // Get declaring interface class from method, and itable index __ load_method_holder(x10, xmethod); From 47b2e994b083f1c53f51fae605a192a6a44f2483 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 9 Feb 2026 14:47:34 +0000 Subject: [PATCH 176/215] 8376199: Convert CodeCacheUnloadingTask to use Atomic Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/shared/parallelCleaning.cpp | 8 ++++---- src/hotspot/share/gc/shared/parallelCleaning.hpp | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/gc/shared/parallelCleaning.cpp b/src/hotspot/share/gc/shared/parallelCleaning.cpp index e302085d0cc..1a0d536f3b3 100644 --- a/src/hotspot/share/gc/shared/parallelCleaning.cpp +++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp @@ -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 @@ -39,7 +39,7 @@ CodeCacheUnloadingTask::CodeCacheUnloadingTask(bool unloading_occurred) : if(iter.next()) { _first_nmethod = iter.method(); } - _claimed_nmethod = _first_nmethod; + _claimed_nmethod.store_relaxed(_first_nmethod); } CodeCacheUnloadingTask::~CodeCacheUnloadingTask() { @@ -53,7 +53,7 @@ void CodeCacheUnloadingTask::claim_nmethods(nmethod** claimed_nmethods, int *num do { *num_claimed_nmethods = 0; - first = _claimed_nmethod; + first = _claimed_nmethod.load_relaxed(); last = NMethodIterator(NMethodIterator::all, first); if (first != nullptr) { @@ -67,7 +67,7 @@ void CodeCacheUnloadingTask::claim_nmethods(nmethod** claimed_nmethods, int *num } } - } while (AtomicAccess::cmpxchg(&_claimed_nmethod, first, last.method()) != first); + } while (!_claimed_nmethod.compare_set(first, last.method())); } void CodeCacheUnloadingTask::work(uint worker_id) { diff --git a/src/hotspot/share/gc/shared/parallelCleaning.hpp b/src/hotspot/share/gc/shared/parallelCleaning.hpp index ed76c4c9df9..0f5cb78bf55 100644 --- a/src/hotspot/share/gc/shared/parallelCleaning.hpp +++ b/src/hotspot/share/gc/shared/parallelCleaning.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 @@ -29,6 +29,7 @@ #include "code/codeCache.hpp" #include "gc/shared/oopStorageParState.hpp" #include "gc/shared/workerThread.hpp" +#include "runtime/atomic.hpp" class CodeCacheUnloadingTask { @@ -36,7 +37,7 @@ class CodeCacheUnloadingTask { // Variables used to claim nmethods. nmethod* _first_nmethod; - nmethod* volatile _claimed_nmethod; + Atomic _claimed_nmethod; public: CodeCacheUnloadingTask(bool unloading_occurred); From f81bea29a3595195d747068adea2a427cf26385e Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 9 Feb 2026 14:47:55 +0000 Subject: [PATCH 177/215] 8376351: Parallel: Convert ParallelScavengeHeap to use Atomic Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 0f55de90a8a..f49419595e1 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -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 @@ -48,6 +48,7 @@ #include "memory/reservedSpace.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" +#include "runtime/atomic.hpp" #include "runtime/cpuTimeCounters.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" @@ -594,7 +595,7 @@ void ParallelScavengeHeap::object_iterate(ObjectClosure* cl) { // these spaces. // The old space is divided into fixed-size blocks. class HeapBlockClaimer : public StackObj { - size_t _claimed_index; + Atomic _claimed_index; public: static const size_t InvalidIndex = SIZE_MAX; @@ -606,7 +607,7 @@ public: // Claim the block and get the block index. size_t claim_and_get_block() { size_t block_index; - block_index = AtomicAccess::fetch_then_add(&_claimed_index, 1u); + block_index = _claimed_index.fetch_then_add(1u); PSOldGen* old_gen = ParallelScavengeHeap::heap()->old_gen(); size_t num_claims = old_gen->num_iterable_blocks() + NumNonOldGenClaims; From 2a8badf5a6c956536fc0b4d55992f213409808c2 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 9 Feb 2026 14:50:26 +0000 Subject: [PATCH 178/215] 8376356: Parallel: Convert PSCardTable to use Atomic Reviewed-by: iwalulya, ayang --- src/hotspot/share/gc/parallel/psCardTable.cpp | 6 +++--- src/hotspot/share/gc/parallel/psCardTable.hpp | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/gc/parallel/psCardTable.cpp b/src/hotspot/share/gc/parallel/psCardTable.cpp index fcd0dd67a45..6429766309a 100644 --- a/src/hotspot/share/gc/parallel/psCardTable.cpp +++ b/src/hotspot/share/gc/parallel/psCardTable.cpp @@ -108,7 +108,7 @@ void PSCardTable::scan_obj_with_limit(PSPromotionManager* pm, } void PSCardTable::pre_scavenge(uint active_workers) { - _preprocessing_active_workers = active_workers; + _preprocessing_active_workers.store_relaxed(active_workers); } // The "shadow" table is a copy of the card table entries of the current stripe. @@ -382,9 +382,9 @@ void PSCardTable::scavenge_contents_parallel(ObjectStartArray* start_array, preprocess_card_table_parallel(object_start, old_gen_bottom, old_gen_top, stripe_index, n_stripes); // Sync with other workers. - AtomicAccess::dec(&_preprocessing_active_workers); + _preprocessing_active_workers.sub_then_fetch(1); SpinYield spin_yield; - while (AtomicAccess::load_acquire(&_preprocessing_active_workers) > 0) { + while (_preprocessing_active_workers.load_acquire() > 0) { spin_yield.wait(); } diff --git a/src/hotspot/share/gc/parallel/psCardTable.hpp b/src/hotspot/share/gc/parallel/psCardTable.hpp index 70c32d23b7f..033933bcbf1 100644 --- a/src/hotspot/share/gc/parallel/psCardTable.hpp +++ b/src/hotspot/share/gc/parallel/psCardTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, 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 @@ -27,6 +27,7 @@ #include "gc/shared/cardTable.hpp" #include "oops/oop.hpp" +#include "runtime/atomic.hpp" class MutableSpace; class ObjectStartArray; @@ -37,7 +38,7 @@ class PSCardTable: public CardTable { static constexpr size_t num_cards_in_stripe = 128; static_assert(num_cards_in_stripe >= 1, "progress"); - volatile int _preprocessing_active_workers; + Atomic _preprocessing_active_workers; bool is_dirty(CardValue* card) { return !is_clean(card); From 36758b0839a5ad92af556fc06cbfd207d61a0950 Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Mon, 9 Feb 2026 15:49:53 +0000 Subject: [PATCH 179/215] 8377435: Problem list serviceability/sa/TestJhsdbJstackMixedCore.java Reviewed-by: dholmes, ysuenaga --- test/hotspot/jtreg/ProblemList.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 3e4814180f6..147d1ce2d78 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -140,6 +140,7 @@ serviceability/sa/TestJmapCore.java 8318754 macosx-aarch64 serviceability/sa/TestJmapCoreMetaspace.java 8318754 macosx-aarch64 serviceability/sa/ClhsdbThreadContext.java 8356704 windows-x64 +serviceability/sa/TestJhsdbJstackMixedCore.java 8377395 linux-x64 serviceability/jvmti/stress/StackTrace/NotSuspended/GetStackTraceNotSuspendedStressTest.java 8315980 linux-all,windows-x64 From 3065aa48c9d72bb0c4e2e14a866ec7d9b5515b35 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Mon, 9 Feb 2026 17:26:32 +0000 Subject: [PATCH 180/215] 8376627: Remove AppContext from javax/swing/plaf/metal classes Reviewed-by: serb, psadhukhan --- .../javax/swing/plaf/metal/MetalBumps.java | 17 +- .../javax/swing/plaf/metal/MetalButtonUI.java | 14 +- .../swing/plaf/metal/MetalCheckBoxUI.java | 15 +- .../swing/plaf/metal/MetalLookAndFeel.java | 18 +- .../swing/plaf/metal/MetalRadioButtonUI.java | 14 +- .../swing/plaf/metal/MetalToggleButtonUI.java | 14 +- .../plaf/metal/MetalBumps/Test6657026.java | 240 ------------------ .../MetalInternalFrameUI/Test6657026.java | 61 ----- .../plaf/metal/MetalSliderUI/Test6657026.java | 67 ----- 9 files changed, 24 insertions(+), 436 deletions(-) delete mode 100644 test/jdk/javax/swing/plaf/metal/MetalBumps/Test6657026.java delete mode 100644 test/jdk/javax/swing/plaf/metal/MetalInternalFrameUI/Test6657026.java delete mode 100644 test/jdk/javax/swing/plaf/metal/MetalSliderUI/Test6657026.java diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBumps.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBumps.java index dddcde10636..65030716d49 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBumps.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBumps.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,7 +30,6 @@ import java.awt.image.*; import javax.swing.*; import java.util.ArrayList; import java.util.List; -import sun.awt.AppContext; /** * Implements the bumps used throughout the Metal Look and Feel. @@ -50,7 +49,7 @@ class MetalBumps implements Icon { protected Color shadowColor; protected Color backColor; - private static final Object METAL_BUMPS = new Object(); + private static final List bumpsList = new ArrayList(); protected BumpBuffer buffer; /** @@ -66,20 +65,14 @@ class MetalBumps implements Icon { private static BumpBuffer createBuffer(GraphicsConfiguration gc, Color topColor, Color shadowColor, Color backColor) { - AppContext context = AppContext.getAppContext(); - @SuppressWarnings("unchecked") - List buffers = (List) context.get(METAL_BUMPS); - if (buffers == null) { - buffers = new ArrayList(); - context.put(METAL_BUMPS, buffers); - } - for (BumpBuffer buffer : buffers) { + + for (BumpBuffer buffer : bumpsList) { if (buffer.hasSameConfiguration(gc, topColor, shadowColor, backColor)) { return buffer; } } BumpBuffer buffer = new BumpBuffer(gc, topColor, shadowColor, backColor); - buffers.add(buffer); + bumpsList.add(buffer); return buffer; } diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalButtonUI.java index c940b4dcead..92b7c3b58f9 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,7 +26,6 @@ package javax.swing.plaf.metal; import sun.swing.SwingUtilities2; -import sun.awt.AppContext; import javax.swing.*; import javax.swing.border.*; @@ -69,7 +68,7 @@ public class MetalButtonUI extends BasicButtonUI { */ protected Color disabledTextColor; - private static final Object METAL_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new MetalButtonUI(); // ******************************** // Create PLAF @@ -87,14 +86,7 @@ public class MetalButtonUI extends BasicButtonUI { * @return an instance of {@code MetalButtonUI} */ public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - MetalButtonUI metalButtonUI = - (MetalButtonUI) appContext.get(METAL_BUTTON_UI_KEY); - if (metalButtonUI == null) { - metalButtonUI = new MetalButtonUI(); - appContext.put(METAL_BUTTON_UI_KEY, metalButtonUI); - } - return metalButtonUI; + return UI; } // ******************************** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalCheckBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalCheckBoxUI.java index 558f1987e4d..4809de43a8f 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalCheckBoxUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,6 @@ package javax.swing.plaf.metal; -import sun.awt.AppContext; - import javax.swing.*; import javax.swing.plaf.basic.BasicCheckBoxUI; @@ -57,7 +55,7 @@ public class MetalCheckBoxUI extends MetalRadioButtonUI { // of BasicCheckBoxUI because we want to pick up all the // painting changes made in MetalRadioButtonUI. - private static final Object METAL_CHECK_BOX_UI_KEY = new Object(); + private static final ComponentUI UI = new MetalCheckBoxUI(); private static final String propertyPrefix = "CheckBox" + "."; @@ -79,14 +77,7 @@ public class MetalCheckBoxUI extends MetalRadioButtonUI { * @return a new instance of {@code MetalCheckBoxUI} */ public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - MetalCheckBoxUI checkboxUI = - (MetalCheckBoxUI) appContext.get(METAL_CHECK_BOX_UI_KEY); - if (checkboxUI == null) { - checkboxUI = new MetalCheckBoxUI(); - appContext.put(METAL_CHECK_BOX_UI_KEY, checkboxUI); - } - return checkboxUI; + return UI; } public String getPropertyPrefix() { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java index 7b9a23aec5d..7c56c681423 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -61,7 +61,6 @@ import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicLookAndFeel; import javax.swing.text.DefaultEditorKit; -import sun.awt.AppContext; import sun.awt.OSInfo; import sun.awt.SunToolkit; import sun.swing.DefaultLayoutStyle; @@ -1600,6 +1599,8 @@ public class MetalLookAndFeel extends BasicLookAndFeel super.provideErrorFeedback(component); } + private static MetalTheme currentMetalTheme; + /** * Set the theme used by MetalLookAndFeel. *

      @@ -1629,7 +1630,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel if (theme == null) { throw new NullPointerException("Can't have null theme"); } - AppContext.getAppContext().put( "currentMetalTheme", theme ); + currentMetalTheme = theme; } /** @@ -1641,15 +1642,10 @@ public class MetalLookAndFeel extends BasicLookAndFeel * @since 1.5 */ public static MetalTheme getCurrentTheme() { - MetalTheme currentTheme; - AppContext context = AppContext.getAppContext(); - currentTheme = (MetalTheme) context.get( "currentMetalTheme" ); + MetalTheme currentTheme = currentMetalTheme; if (currentTheme == null) { - // This will happen in two cases: - // . When MetalLookAndFeel is first being initialized. - // . When a new AppContext has been created that hasn't - // triggered UIManager to load a LAF. Rather than invoke - // a method on the UIManager, which would trigger the loading + // This will happen when MetalLookAndFeel is first being initialized. + // Rather than invoke a method on the UIManager, which would trigger the loading // of a potentially different LAF, we directly set the // Theme here. if (useHighContrastTheme()) { diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalRadioButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalRadioButtonUI.java index 712046737eb..ddbd7bcc9cb 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalRadioButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,7 +26,6 @@ package javax.swing.plaf.metal; import sun.swing.SwingUtilities2; -import sun.awt.AppContext; import java.awt.*; import java.awt.event.*; @@ -55,7 +54,7 @@ import javax.swing.text.View; */ public class MetalRadioButtonUI extends BasicRadioButtonUI { - private static final Object METAL_RADIO_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new MetalRadioButtonUI(); /** * The color of the focused radio button. @@ -90,14 +89,7 @@ public class MetalRadioButtonUI extends BasicRadioButtonUI { * @return an instance of {@code MetalRadioButtonUI} */ public static ComponentUI createUI(JComponent c) { - AppContext appContext = AppContext.getAppContext(); - MetalRadioButtonUI metalRadioButtonUI = - (MetalRadioButtonUI) appContext.get(METAL_RADIO_BUTTON_UI_KEY); - if (metalRadioButtonUI == null) { - metalRadioButtonUI = new MetalRadioButtonUI(); - appContext.put(METAL_RADIO_BUTTON_UI_KEY, metalRadioButtonUI); - } - return metalRadioButtonUI; + return UI; } // ******************************** diff --git a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalToggleButtonUI.java b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalToggleButtonUI.java index 088a54ba6b2..9c944388c4a 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalToggleButtonUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/metal/MetalToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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,7 +26,6 @@ package javax.swing.plaf.metal; import sun.swing.SwingUtilities2; -import sun.awt.AppContext; import java.awt.*; import java.awt.event.*; @@ -57,7 +56,7 @@ import java.io.Serializable; */ public class MetalToggleButtonUI extends BasicToggleButtonUI { - private static final Object METAL_TOGGLE_BUTTON_UI_KEY = new Object(); + private static final ComponentUI UI = new MetalToggleButtonUI(); /** * The color of a focused toggle button. @@ -92,14 +91,7 @@ public class MetalToggleButtonUI extends BasicToggleButtonUI { * @return the {@code MetalToggleButtonUI}. */ public static ComponentUI createUI(JComponent b) { - AppContext appContext = AppContext.getAppContext(); - MetalToggleButtonUI metalToggleButtonUI = - (MetalToggleButtonUI) appContext.get(METAL_TOGGLE_BUTTON_UI_KEY); - if (metalToggleButtonUI == null) { - metalToggleButtonUI = new MetalToggleButtonUI(); - appContext.put(METAL_TOGGLE_BUTTON_UI_KEY, metalToggleButtonUI); - } - return metalToggleButtonUI; + return UI; } // ******************************** diff --git a/test/jdk/javax/swing/plaf/metal/MetalBumps/Test6657026.java b/test/jdk/javax/swing/plaf/metal/MetalBumps/Test6657026.java deleted file mode 100644 index b5737479325..00000000000 --- a/test/jdk/javax/swing/plaf/metal/MetalBumps/Test6657026.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please 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 6657026 - * @summary Tests shared MetalBumps in different application contexts - * @author Sergey Malenkov - * @modules java.desktop/sun.awt - */ - -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.image.BufferedImage; -import java.awt.image.ImageObserver; -import java.text.AttributedCharacterIterator; - -import javax.swing.JToolBar; -import javax.swing.plaf.metal.MetalBorders.ToolBarBorder; - -import sun.awt.SunToolkit; - -public class Test6657026 extends ToolBarBorder implements Runnable { - - public static void main(String[] args) throws Exception { - new Test6657026().test(); - - ThreadGroup group = new ThreadGroup("$$$"); - Thread thread = new Thread(group, new Test6657026()); - thread.start(); - thread.join(); - } - - public void run() { - SunToolkit.createNewAppContext(); - test(); - } - - private void test() { - MyGraphics mg = new MyGraphics(); - ToolBarBorder border = new ToolBarBorder(); - border.paintBorder(mg.component, mg, 0, 0, 10, 10); - if (mg.image != null) { - boolean failed = true; - int value = mg.image.getRGB(0, 0); - for (int x = 0; x < mg.image.getWidth(); x++) { - for (int y = 0; y < mg.image.getHeight(); y++) { - int current = mg.image.getRGB(x, y); - if (current != value) { - mg.image.setRGB(x, y, value); - failed = false; - } - - } - } - if (failed) { - throw new Error("shared metal bumps"); - } - } - } - - private static class MyGraphics extends Graphics { - - private final Component component = new JToolBar() {}; - private BufferedImage image; - - public Graphics create() { - return null; // TODO: check - } - - public void translate(int x, int y) { - // TODO: check - } - - public Color getColor() { - return null; // TODO: check - } - - public void setColor(Color color) { - // TODO: check - } - - public void setPaintMode() { - // TODO: check - } - - public void setXORMode(Color c1) { - // TODO: check - } - - public Font getFont() { - return null; // TODO: check - } - - public void setFont(Font font) { - // TODO: check - } - - public FontMetrics getFontMetrics(Font font) { - return null; // TODO: check - } - - public Rectangle getClipBounds() { - return null; // TODO: check - } - - public void clipRect(int x, int y, int width, int height) { - // TODO: check - } - - public void setClip(int x, int y, int width, int height) { - // TODO: check - } - - public Shape getClip() { - return null; // TODO: check - } - - public void setClip(Shape clip) { - // TODO: check - } - - public void copyArea(int x, int y, int width, int height, int dx, int dy) { - // TODO: check - } - - public void drawLine(int x1, int y1, int x2, int y2) { - // TODO: check - } - - public void fillRect(int x, int y, int width, int height) { - // TODO: check - } - - public void clearRect(int x, int y, int width, int height) { - // TODO: check - } - - public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - // TODO: check - } - - public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - // TODO: check - } - - public void drawOval(int x, int y, int width, int height) { - // TODO: check - } - - public void fillOval(int x, int y, int width, int height) { - // TODO: check - } - - public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - // TODO: check - } - - public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - // TODO: check - } - - public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) { - // TODO: check - } - - public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) { - // TODO: check - } - - public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) { - // TODO: check - } - - public void drawString(String str, int x, int y) { - // TODO: check - } - - public void drawString(AttributedCharacterIterator iterator, int x, int y) { - // TODO: check - } - - public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - return false; // TODO: check - } - - public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { - return false; // TODO: check - } - - public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { - return false; // TODO: check - } - - public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { - return false; // TODO: check - } - - public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { - if (img instanceof BufferedImage) { - this.image = (BufferedImage) img; - } - return false; // TODO: check - } - - public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { - return false; // TODO: check - } - - public void dispose() { - // TODO: check - } - } -} diff --git a/test/jdk/javax/swing/plaf/metal/MetalInternalFrameUI/Test6657026.java b/test/jdk/javax/swing/plaf/metal/MetalInternalFrameUI/Test6657026.java deleted file mode 100644 index 158c743375c..00000000000 --- a/test/jdk/javax/swing/plaf/metal/MetalInternalFrameUI/Test6657026.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 6657026 - * @summary Tests shared MetalInternalFrameUI in different application contexts - * @author Sergey Malenkov - * @modules java.desktop/sun.awt - */ - -import sun.awt.SunToolkit; - -import javax.swing.JInternalFrame; -import javax.swing.JPanel; -import javax.swing.UIManager; -import javax.swing.plaf.metal.MetalInternalFrameUI; -import javax.swing.plaf.metal.MetalLookAndFeel; - -public class Test6657026 extends MetalInternalFrameUI implements Runnable { - - public static void main(String[] args) throws Exception { - UIManager.setLookAndFeel(new MetalLookAndFeel()); - - ThreadGroup group = new ThreadGroup("$$$"); - Thread thread = new Thread(group, new Test6657026()); - thread.start(); - thread.join(); - - new JInternalFrame().setContentPane(new JPanel()); - } - - public Test6657026() { - super(null); - } - - public void run() { - SunToolkit.createNewAppContext(); - IS_PALETTE = JInternalFrame.CONTENT_PANE_PROPERTY; - } -} diff --git a/test/jdk/javax/swing/plaf/metal/MetalSliderUI/Test6657026.java b/test/jdk/javax/swing/plaf/metal/MetalSliderUI/Test6657026.java deleted file mode 100644 index b8ea5e2bd07..00000000000 --- a/test/jdk/javax/swing/plaf/metal/MetalSliderUI/Test6657026.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 6657026 7077259 - * @summary Tests shared MetalSliderUI in different application contexts - * @author Sergey Malenkov - * @modules java.desktop/sun.awt - * @run main/othervm -Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel Test6657026 - */ - -import javax.swing.JSlider; -import javax.swing.UIManager; -import javax.swing.plaf.metal.MetalLookAndFeel; -import javax.swing.plaf.metal.MetalSliderUI; -import sun.awt.SunToolkit; - -public class Test6657026 extends MetalSliderUI implements Runnable { - - public static void main(String[] args) throws Exception { - JSlider slider = new JSlider(); - test(slider); - - ThreadGroup group = new ThreadGroup("$$$"); - Thread thread = new Thread(group, new Test6657026()); - thread.start(); - thread.join(); - - test(slider); - } - - public void run() { - SunToolkit.createNewAppContext(); - JSlider slider = new JSlider(); - test(slider); - tickLength = -10000; - } - - private static void test(JSlider slider) { - MetalSliderUI ui = (MetalSliderUI) slider.getUI(); - int actual = ui.getTickLength(); - if (actual != 11) { - throw new Error(actual + ", but expected 11"); - } - } -} From 3871b8899df79fa85619975bd1c7f59792a839d1 Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Mon, 9 Feb 2026 17:43:07 +0000 Subject: [PATCH 181/215] 8375065: Update LCMS to 2.18 Reviewed-by: prr, serb, jdv --- src/java.desktop/share/legal/lcms.md | 4 +- .../share/native/liblcms/cmsalpha.c | 4 +- .../share/native/liblcms/cmscam02.c | 41 +++++++++-------- .../share/native/liblcms/cmscgats.c | 13 ++++-- .../share/native/liblcms/cmscnvrt.c | 2 +- .../share/native/liblcms/cmserr.c | 2 +- .../share/native/liblcms/cmsgamma.c | 3 +- .../share/native/liblcms/cmsgmt.c | 2 +- .../share/native/liblcms/cmshalf.c | 2 +- .../share/native/liblcms/cmsintrp.c | 10 ++-- .../share/native/liblcms/cmsio0.c | 12 +++-- .../share/native/liblcms/cmsio1.c | 10 +++- .../share/native/liblcms/cmslut.c | 2 +- .../share/native/liblcms/cmsmd5.c | 2 +- .../share/native/liblcms/cmsmtrx.c | 2 +- .../share/native/liblcms/cmsnamed.c | 16 +++---- .../share/native/liblcms/cmsopt.c | 16 +++++-- .../share/native/liblcms/cmspack.c | 2 +- .../share/native/liblcms/cmspcs.c | 2 +- .../share/native/liblcms/cmsplugin.c | 4 +- .../share/native/liblcms/cmsps2.c | 2 +- .../share/native/liblcms/cmssamp.c | 13 ++++-- src/java.desktop/share/native/liblcms/cmssm.c | 2 +- .../share/native/liblcms/cmstypes.c | 40 +++++++++------- .../share/native/liblcms/cmsvirt.c | 46 +++++++++++-------- .../share/native/liblcms/cmswtpnt.c | 2 +- .../share/native/liblcms/cmsxform.c | 9 +++- src/java.desktop/share/native/liblcms/lcms2.h | 6 +-- .../share/native/liblcms/lcms2_internal.h | 2 +- .../share/native/liblcms/lcms2_plugin.h | 2 +- 30 files changed, 167 insertions(+), 108 deletions(-) diff --git a/src/java.desktop/share/legal/lcms.md b/src/java.desktop/share/legal/lcms.md index d3bd7d84c8b..20a22ea4db5 100644 --- a/src/java.desktop/share/legal/lcms.md +++ b/src/java.desktop/share/legal/lcms.md @@ -1,11 +1,11 @@ -## Little Color Management System (LCMS) v2.17 +## Little Color Management System (LCMS) v2.18 ### LCMS License

       
       MIT License
       
      -Copyright (C) 1998-2025 Marti Maria Saguer
      +Copyright (C) 1998-2026 Marti Maria Saguer
       
       Permission is hereby granted, free of charge, to any person obtaining
       a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmsalpha.c b/src/java.desktop/share/native/liblcms/cmsalpha.c
      index 2e50b65be24..bcedbde938e 100644
      --- a/src/java.desktop/share/native/liblcms/cmsalpha.c
      +++ b/src/java.desktop/share/native/liblcms/cmsalpha.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -406,7 +406,7 @@ int FormatterPos(cmsUInt32Number frm)
       static
       cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out)
       {
      -static cmsFormatterAlphaFn FormattersAlpha[6][6] = {
      +static const cmsFormatterAlphaFn FormattersAlpha[6][6] = {
       
              /* from 8 */  { copy8,       from8to16,   from8to16SE,   from8toHLF,   from8toFLT,    from8toDBL    },
              /* from 16*/  { from16to8,   copy16,      from16to16,    from16toHLF,  from16toFLT,   from16toDBL   },
      diff --git a/src/java.desktop/share/native/liblcms/cmscam02.c b/src/java.desktop/share/native/liblcms/cmscam02.c
      index 45ef4eef970..168ef597032 100644
      --- a/src/java.desktop/share/native/liblcms/cmscam02.c
      +++ b/src/java.desktop/share/native/liblcms/cmscam02.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -285,27 +285,32 @@ CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod)
                  (clr.J / 100.0),
                  (1.0 / (pMod->c * pMod->z)));
       
      -    p1 = e / t;
           p2 = (clr.A / pMod->Nbb) + 0.305;
      -    p3 = 21.0 / 20.0;
       
      -    hr = clr.h * d2r;
      -
      -    if (fabs(sin(hr)) >= fabs(cos(hr))) {
      -        p4 = p1 / sin(hr);
      -        clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
      -            (p4 + (2.0 + p3) * (220.0 / 1403.0) *
      -            (cos(hr) / sin(hr)) - (27.0 / 1403.0) +
      -            p3 * (6300.0 / 1403.0));
      -        clr.a = clr.b * (cos(hr) / sin(hr));
      +    if ( t <= 0.0 ) {     // special case from spec notes, avoid divide by zero
      +        clr.a = clr.b = 0.0;
           }
           else {
      -        p5 = p1 / cos(hr);
      -        clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
      -            (p5 + (2.0 + p3) * (220.0 / 1403.0) -
      -            ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *
      -            (sin(hr) / cos(hr)));
      -        clr.b = clr.a * (sin(hr) / cos(hr));
      +        hr = clr.h * d2r;
      +        p1 = e / t;
      +        p3 = 21.0 / 20.0;
      +
      +        if (fabs(sin(hr)) >= fabs(cos(hr))) {
      +            p4 = p1 / sin(hr);
      +            clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
      +                (p4 + (2.0 + p3) * (220.0 / 1403.0) *
      +                (cos(hr) / sin(hr)) - (27.0 / 1403.0) +
      +                p3 * (6300.0 / 1403.0));
      +            clr.a = clr.b * (cos(hr) / sin(hr));
      +        }
      +        else {
      +            p5 = p1 / cos(hr);
      +            clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
      +                (p5 + (2.0 + p3) * (220.0 / 1403.0) -
      +                ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *
      +                (sin(hr) / cos(hr)));
      +            clr.b = clr.a * (sin(hr) / cos(hr));
      +        }
           }
       
           clr.RGBpa[0] = ((460.0 / 1403.0) * p2) +
      diff --git a/src/java.desktop/share/native/liblcms/cmscgats.c b/src/java.desktop/share/native/liblcms/cmscgats.c
      index 3e62d064c3f..e8a75c7355f 100644
      --- a/src/java.desktop/share/native/liblcms/cmscgats.c
      +++ b/src/java.desktop/share/native/liblcms/cmscgats.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -295,7 +295,7 @@ typedef struct {
               WRITEMODE as;      // How is supposed to be written
           } PROPERTY;
       
      -static PROPERTY PredefinedProperties[] = {
      +static const PROPERTY PredefinedProperties[] = {
       
               {"NUMBER_OF_FIELDS", WRITE_UNCOOKED},    // Required - NUMBER OF FIELDS
               {"NUMBER_OF_SETS",   WRITE_UNCOOKED},    // Required - NUMBER OF SETS
      @@ -458,7 +458,7 @@ cmsBool StringAppend(string* s, char c)
               new_ptr = (char*) AllocChunk(s->it8, s->max);
               if (new_ptr == NULL) return FALSE;
       
      -        if (new_ptr != NULL && s->begin != NULL)
      +        if (s->begin != NULL)
                   memcpy(new_ptr, s->begin, s->len);
       
               s->begin = new_ptr;
      @@ -899,6 +899,11 @@ void InSymbol(cmsIT8* it8)
                           sign = -1;
                           NextCh(it8);
                       }
      +                else
      +                    if (it8->ch == '+') {
      +                        sign = +1;
      +                        NextCh(it8);
      +                    }
       
                       it8->inum = 0;
                       it8->sy   = SINUM;
      @@ -3206,7 +3211,7 @@ cmsBool ParseCube(cmsIT8* cube, cmsStage** Shaper, cmsStage** CLUT, char title[]
       
                       int nodes = lut_size * lut_size * lut_size;
       
      -                cmsFloat32Number* lut_table = _cmsMalloc(cube->ContextID, nodes * 3 * sizeof(cmsFloat32Number));
      +                cmsFloat32Number* lut_table = (cmsFloat32Number*) _cmsMalloc(cube->ContextID, nodes * 3 * sizeof(cmsFloat32Number));
                       if (lut_table == NULL) return FALSE;
       
                       for (i = 0; i < nodes; i++) {
      diff --git a/src/java.desktop/share/native/liblcms/cmscnvrt.c b/src/java.desktop/share/native/liblcms/cmscnvrt.c
      index 9f8619cb9da..c66dbcbebad 100644
      --- a/src/java.desktop/share/native/liblcms/cmscnvrt.c
      +++ b/src/java.desktop/share/native/liblcms/cmscnvrt.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmserr.c b/src/java.desktop/share/native/liblcms/cmserr.c
      index d421c550d32..877beb9ca6a 100644
      --- a/src/java.desktop/share/native/liblcms/cmserr.c
      +++ b/src/java.desktop/share/native/liblcms/cmserr.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmsgamma.c b/src/java.desktop/share/native/liblcms/cmsgamma.c
      index 773858b0c1f..bace6ab02e2 100644
      --- a/src/java.desktop/share/native/liblcms/cmsgamma.c
      +++ b/src/java.desktop/share/native/liblcms/cmsgamma.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -1187,6 +1187,7 @@ cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[]
           cmsFloat32Number *c, *d, *e;
           cmsBool st;
       
      +    if (m < 4 || lambda < MATRIX_DET_TOLERANCE) return FALSE;
       
           c = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number));
           d = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number));
      diff --git a/src/java.desktop/share/native/liblcms/cmsgmt.c b/src/java.desktop/share/native/liblcms/cmsgmt.c
      index 03ac70202a5..1b023dcc299 100644
      --- a/src/java.desktop/share/native/liblcms/cmsgmt.c
      +++ b/src/java.desktop/share/native/liblcms/cmsgmt.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmshalf.c b/src/java.desktop/share/native/liblcms/cmshalf.c
      index 7e5f7a3c7e0..e1fb1d55488 100644
      --- a/src/java.desktop/share/native/liblcms/cmshalf.c
      +++ b/src/java.desktop/share/native/liblcms/cmshalf.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmsintrp.c b/src/java.desktop/share/native/liblcms/cmsintrp.c
      index 43c47429c3c..23e59a229a9 100644
      --- a/src/java.desktop/share/native/liblcms/cmsintrp.c
      +++ b/src/java.desktop/share/native/liblcms/cmsintrp.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -984,9 +984,9 @@ void Eval4Inputs(CMSREGISTER const cmsUInt16Number Input[],
                                       c1 = c2 = c3 = 0;
                                   }
       
      -        Rest = c1 * rx + c2 * ry + c3 * rz;
      +        Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
       
      -        Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
      +        Tmp1[OutChan] = (cmsUInt16Number)c0 + ((Rest + (Rest >> 16)) >> 16);
           }
       
       
      @@ -1048,9 +1048,9 @@ void Eval4Inputs(CMSREGISTER const cmsUInt16Number Input[],
                                       c1 = c2 = c3 = 0;
                                   }
       
      -        Rest = c1 * rx + c2 * ry + c3 * rz;
      +        Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
       
      -        Tmp2[OutChan] = (cmsUInt16Number) (c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)));
      +        Tmp2[OutChan] = (cmsUInt16Number) c0 + ((Rest + (Rest >> 16)) >> 16);
           }
       
       
      diff --git a/src/java.desktop/share/native/liblcms/cmsio0.c b/src/java.desktop/share/native/liblcms/cmsio0.c
      index 5258b7939d2..5a4f09af5bc 100644
      --- a/src/java.desktop/share/native/liblcms/cmsio0.c
      +++ b/src/java.desktop/share/native/liblcms/cmsio0.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -685,6 +685,7 @@ void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i)
               // Free previous version
               if (Icc ->TagSaveAsRaw[i]) {
                   _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);
      +            Icc->TagSaveAsRaw[i] = FALSE;
               }
               else {
                   cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];
      @@ -1605,6 +1606,8 @@ void freeOneTag(_cmsICCPROFILE* Icc, cmsUInt32Number i)
               else
                   _cmsFree(Icc->ContextID, Icc->TagPtrs[i]);
           }
      +
      +    Icc->TagPtrs[i] = NULL;
       }
       
       // Closes a profile freeing any involved resources
      @@ -1847,8 +1850,11 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v
       
           if (!_cmsNewTag(Icc, sig, &i)) goto Error;
       
      -    // This is not raw
      -    Icc ->TagSaveAsRaw[i] = FALSE;
      +    // This cannot be RAW
      +    if (Icc->TagSaveAsRaw[i]) {
      +        cmsSignalError(Icc->ContextID, cmsERROR_ALREADY_DEFINED, "Tag  '%x' was already saved as RAW", sig);
      +        goto Error;
      +    }
       
           // This is not a link
           Icc ->TagLinked[i] = (cmsTagSignature) 0;
      diff --git a/src/java.desktop/share/native/liblcms/cmsio1.c b/src/java.desktop/share/native/liblcms/cmsio1.c
      index 48772c7cbde..463f1192c2a 100644
      --- a/src/java.desktop/share/native/liblcms/cmsio1.c
      +++ b/src/java.desktop/share/native/liblcms/cmsio1.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -1012,7 +1012,13 @@ const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info)
           switch (Info) {
       
           case cmsInfoDescription:
      -        sig = cmsSigProfileDescriptionTag;
      +        /**
      +        * Add for MacOS, which uses propiertary tags for description
      +        */
      +        if (cmsIsTag(hProfile, cmsSigProfileDescriptionMLTag))
      +            sig = cmsSigProfileDescriptionMLTag;
      +        else
      +            sig = cmsSigProfileDescriptionTag;
               break;
       
           case cmsInfoManufacturer:
      diff --git a/src/java.desktop/share/native/liblcms/cmslut.c b/src/java.desktop/share/native/liblcms/cmslut.c
      index 3cf4e8cac5a..28220eae667 100644
      --- a/src/java.desktop/share/native/liblcms/cmslut.c
      +++ b/src/java.desktop/share/native/liblcms/cmslut.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmsmd5.c b/src/java.desktop/share/native/liblcms/cmsmd5.c
      index d9b9a4e5260..f18300ebace 100644
      --- a/src/java.desktop/share/native/liblcms/cmsmd5.c
      +++ b/src/java.desktop/share/native/liblcms/cmsmd5.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmsmtrx.c b/src/java.desktop/share/native/liblcms/cmsmtrx.c
      index 841da662a10..1db000752e3 100644
      --- a/src/java.desktop/share/native/liblcms/cmsmtrx.c
      +++ b/src/java.desktop/share/native/liblcms/cmsmtrx.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmsnamed.c b/src/java.desktop/share/native/liblcms/cmsnamed.c
      index 451bfe9f34d..acdaabc3ec2 100644
      --- a/src/java.desktop/share/native/liblcms/cmsnamed.c
      +++ b/src/java.desktop/share/native/liblcms/cmsnamed.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -303,7 +303,7 @@ cmsUInt32Number encodeUTF8(char* out, const wchar_t* in, cmsUInt32Number max_wch
           cmsUInt32Number size = 0;
           cmsUInt32Number len_w = 0;
       
      -    while (*in && len_w < max_wchars)
      +    while (len_w < max_wchars && *in)
           {
               if (*in >= 0xd800 && *in <= 0xdbff)
                   codepoint = ((*in - 0xd800) << 10) + 0x10000;
      @@ -1071,17 +1071,17 @@ cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq)
           if (pseq == NULL)
               return NULL;
       
      -    NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ));
      +    NewSeq = (cmsSEQ*)_cmsMallocZero(pseq->ContextID, sizeof(cmsSEQ));
           if (NewSeq == NULL) return NULL;
       
      +    NewSeq->ContextID = pseq->ContextID;
       
      -    NewSeq -> seq      = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC));
      -    if (NewSeq ->seq == NULL) goto Error;
      +    NewSeq->seq = (cmsPSEQDESC*)_cmsCalloc(pseq->ContextID, pseq->n, sizeof(cmsPSEQDESC));
      +    if (NewSeq->seq == NULL) goto Error;
       
      -    NewSeq -> ContextID = pseq ->ContextID;
      -    NewSeq -> n        = pseq ->n;
      +    NewSeq->n = pseq->n;
       
      -    for (i=0; i < pseq->n; i++) {
      +    for (i = 0; i < pseq->n; i++) {
       
               memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));
       
      diff --git a/src/java.desktop/share/native/liblcms/cmsopt.c b/src/java.desktop/share/native/liblcms/cmsopt.c
      index 767008e68c5..9e71426a332 100644
      --- a/src/java.desktop/share/native/liblcms/cmsopt.c
      +++ b/src/java.desktop/share/native/liblcms/cmsopt.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -698,11 +698,16 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
           if (ColorSpace == (cmsColorSpaceSignature)0 ||
               OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE;
       
      -    nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
      -
           // For empty LUTs, 2 points are enough
           if (cmsPipelineStageCount(*Lut) == 0)
               nGridPoints = 2;
      +    else
      +    {
      +        nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
      +
      +        // Lab16 as input cannot be optimized by a CLUT due to centering issues, thanks to Mike Chaney for discovering this.
      +        if (!(*dwFlags & cmsFLAGS_FORCE_CLUT) && (ColorSpace == cmsSigLabData) && (T_BYTES(*InputFormat) == 2)) return FALSE;
      +    }
       
           Src = *Lut;
       
      @@ -813,6 +818,11 @@ Error:
                   Dest ->OutputChannels,
                   DataSetOut);
       
      +        if (p16 == NULL) {
      +            cmsPipelineFree(Dest);
      +            return FALSE;
      +        }
      +
               _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup);
           }
       
      diff --git a/src/java.desktop/share/native/liblcms/cmspack.c b/src/java.desktop/share/native/liblcms/cmspack.c
      index d430e73051d..b740567af3b 100644
      --- a/src/java.desktop/share/native/liblcms/cmspack.c
      +++ b/src/java.desktop/share/native/liblcms/cmspack.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmspcs.c b/src/java.desktop/share/native/liblcms/cmspcs.c
      index 5f1b1f0d8e6..8c33057721e 100644
      --- a/src/java.desktop/share/native/liblcms/cmspcs.c
      +++ b/src/java.desktop/share/native/liblcms/cmspcs.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmsplugin.c b/src/java.desktop/share/native/liblcms/cmsplugin.c
      index aaad39f52b0..a943c9f4dd9 100644
      --- a/src/java.desktop/share/native/liblcms/cmsplugin.c
      +++ b/src/java.desktop/share/native/liblcms/cmsplugin.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -522,7 +522,7 @@ cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
           va_start(args, frm);
       
           len = vsnprintf((char*) Buffer, 2047, frm, args);
      -    if (len < 0) {
      +    if (len < 0 || len >= 2047) {
               va_end(args);
               return FALSE;   // Truncated, which is a fatal error for us
           }
      diff --git a/src/java.desktop/share/native/liblcms/cmsps2.c b/src/java.desktop/share/native/liblcms/cmsps2.c
      index 476817e9c1a..80f7c8084ae 100644
      --- a/src/java.desktop/share/native/liblcms/cmsps2.c
      +++ b/src/java.desktop/share/native/liblcms/cmsps2.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmssamp.c b/src/java.desktop/share/native/liblcms/cmssamp.c
      index ca5c4a9d693..c54a0d4ea72 100644
      --- a/src/java.desktop/share/native/liblcms/cmssamp.c
      +++ b/src/java.desktop/share/native/liblcms/cmssamp.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -152,9 +152,12 @@ cmsBool  BlackPointAsDarkerColorant(cmsHPROFILE    hInput,
           // Convert black to Lab
           cmsDoTransform(xform, Black, &Lab, 1);
       
      -    // Force it to be neutral, check for inconsistencies
      -    Lab.a = Lab.b = 0;
      -    if (Lab.L > 50 || Lab.L < 0) Lab.L = 0;
      +    if (Lab.L > 95)
      +        Lab.L = 0;  // for synthetical negative profiles
      +    else if (Lab.L < 0)
      +        Lab.L = 0;
      +    else if (Lab.L > 50)
      +        Lab.L = 50;
       
           // Free the resources
           cmsDeleteTransform(xform);
      @@ -352,7 +355,7 @@ cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[]
           if (fabs(a) < 1.0E-10) {
       
               if (fabs(b) < 1.0E-10) return 0;
      -        return cmsmin(0, cmsmax(50, -c/b ));
      +        return cmsmax(0, cmsmin(50, -c/b ));
           }
           else {
       
      diff --git a/src/java.desktop/share/native/liblcms/cmssm.c b/src/java.desktop/share/native/liblcms/cmssm.c
      index e2a810a2669..b79cd85488b 100644
      --- a/src/java.desktop/share/native/liblcms/cmssm.c
      +++ b/src/java.desktop/share/native/liblcms/cmssm.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmstypes.c b/src/java.desktop/share/native/liblcms/cmstypes.c
      index 22514f88226..eab74940cd0 100644
      --- a/src/java.desktop/share/native/liblcms/cmstypes.c
      +++ b/src/java.desktop/share/native/liblcms/cmstypes.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -4786,7 +4786,6 @@ cmsBool ReadMPEElem(struct _cms_typehandler_struct* self,
       
           return TRUE;
       
      -    cmsUNUSED_PARAMETER(SizeOfTag);
           cmsUNUSED_PARAMETER(n);
       }
       
      @@ -4894,9 +4893,11 @@ cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, v
                        goto Error;
               }
       
      +         Before = io ->Tell(io);
      +
               if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error;
               if (!_cmsWriteUInt32Number(io, 0)) goto Error;
      -        Before = io ->Tell(io);
      +
               if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error;
               if (!_cmsWriteAlignment(io)) goto Error;
       
      @@ -5645,9 +5646,7 @@ void* Type_VideoSignal_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER*
       {
           cmsVideoSignalType* cicp = NULL;
       
      -    if (SizeOfTag != 8) return NULL;
      -
      -    if (!_cmsReadUInt32Number(io, NULL)) return NULL;
      +    if (SizeOfTag != 4) return NULL;
       
           cicp = (cmsVideoSignalType*)_cmsCalloc(self->ContextID, 1, sizeof(cmsVideoSignalType));
           if (cicp == NULL) return NULL;
      @@ -5671,7 +5670,6 @@ cmsBool Type_VideoSignal_Write(struct _cms_typehandler_struct* self, cmsIOHANDLE
       {
           cmsVideoSignalType* cicp = (cmsVideoSignalType*)Ptr;
       
      -    if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
           if (!_cmsWriteUInt8Number(io, cicp->ColourPrimaries)) return FALSE;
           if (!_cmsWriteUInt8Number(io, cicp->TransferCharacteristics)) return FALSE;
           if (!_cmsWriteUInt8Number(io, cicp->MatrixCoefficients)) return FALSE;
      @@ -5744,11 +5742,11 @@ void Type_MHC2_Free(struct _cms_typehandler_struct* self, void* Ptr)
       
       void* Type_MHC2_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
       {
      -    cmsMHC2Type* mhc2 = _cmsDupMem(self->ContextID, Ptr, sizeof(cmsMHC2Type));
      +    cmsMHC2Type* mhc2 = (cmsMHC2Type*)_cmsDupMem(self->ContextID, Ptr, sizeof(cmsMHC2Type));
       
      -    mhc2->RedCurve = _cmsDupMem(self->ContextID,   mhc2->RedCurve, mhc2->CurveEntries*sizeof(cmsFloat64Number));
      -    mhc2->GreenCurve = _cmsDupMem(self->ContextID, mhc2->GreenCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
      -    mhc2->BlueCurve = _cmsDupMem(self->ContextID,  mhc2->BlueCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
      +    mhc2->RedCurve = (cmsFloat64Number*) _cmsDupMem(self->ContextID,   mhc2->RedCurve, mhc2->CurveEntries*sizeof(cmsFloat64Number));
      +    mhc2->GreenCurve = (cmsFloat64Number*) _cmsDupMem(self->ContextID, mhc2->GreenCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
      +    mhc2->BlueCurve = (cmsFloat64Number*) _cmsDupMem(self->ContextID,  mhc2->BlueCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
       
           if (mhc2->RedCurve == NULL ||
               mhc2->GreenCurve == NULL ||
      @@ -5786,7 +5784,6 @@ cmsBool Type_MHC2_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
           cmsUInt32Number MatrixOffset;
           cmsUInt32Number OffsetRedTable, OffsetGreenTable, OffsetBlueTable;
       
      -    if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
           if (!_cmsWriteUInt32Number(io, mhc2->CurveEntries)) return FALSE;
       
           if (!_cmsWrite15Fixed16Number(io, mhc2->MinLuminance)) return FALSE;
      @@ -5811,10 +5808,20 @@ cmsBool Type_MHC2_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
           }
       
           OffsetRedTable = io->Tell(io) - BaseOffset;
      +
      +    if(!_cmsWriteUInt32Number(io, cmsSigS15Fixed16ArrayType)) return FALSE;
      +    if(!_cmsWriteUInt32Number(io, 0)) return FALSE;
      +
           if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->RedCurve)) return FALSE;
      +
           OffsetGreenTable = io->Tell(io) - BaseOffset;
      +    if (!_cmsWriteUInt32Number(io, cmsSigS15Fixed16ArrayType)) return FALSE;
      +    if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
           if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->GreenCurve)) return FALSE;
      +
           OffsetBlueTable = io->Tell(io) - BaseOffset;
      +    if (!_cmsWriteUInt32Number(io, cmsSigS15Fixed16ArrayType)) return FALSE;
      +    if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
           if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->BlueCurve)) return FALSE;
       
           if (!io->Seek(io, TablesOffsetPos)) return FALSE;
      @@ -5858,8 +5865,6 @@ void* Type_MHC2_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cms
           cmsUInt32Number MatrixOffset;
           cmsUInt32Number OffsetRedTable, OffsetGreenTable, OffsetBlueTable;
       
      -    if (!_cmsReadUInt32Number(io, NULL)) return NULL;
      -
           mhc2 = (cmsMHC2Type*)_cmsCalloc(self->ContextID, 1, sizeof(cmsMHC2Type));
           if (mhc2 == NULL) return NULL;
       
      @@ -5890,9 +5895,10 @@ void* Type_MHC2_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cms
               if (!ReadDoublesAt(io, BaseOffset + MatrixOffset, 3*4, &mhc2->XYZ2XYZmatrix[0][0])) goto Error;
           }
       
      -    if (!ReadDoublesAt(io, BaseOffset + OffsetRedTable, mhc2->CurveEntries, mhc2->RedCurve)) goto Error;
      -    if (!ReadDoublesAt(io, BaseOffset + OffsetGreenTable, mhc2->CurveEntries, mhc2->GreenCurve)) goto Error;
      -    if (!ReadDoublesAt(io, BaseOffset + OffsetBlueTable, mhc2->CurveEntries, mhc2->BlueCurve)) goto Error;
      +    // Skip sf32 tag and filler (8bytes)
      +    if (!ReadDoublesAt(io, BaseOffset + OffsetRedTable + 8, mhc2->CurveEntries, mhc2->RedCurve)) goto Error;
      +    if (!ReadDoublesAt(io, BaseOffset + OffsetGreenTable + 8, mhc2->CurveEntries, mhc2->GreenCurve)) goto Error;
      +    if (!ReadDoublesAt(io, BaseOffset + OffsetBlueTable + 8, mhc2->CurveEntries, mhc2->BlueCurve)) goto Error;
       
           // Success
           *nItems = 1;
      diff --git a/src/java.desktop/share/native/liblcms/cmsvirt.c b/src/java.desktop/share/native/liblcms/cmsvirt.c
      index 1ef86dae054..0dfc6e947a5 100644
      --- a/src/java.desktop/share/native/liblcms/cmsvirt.c
      +++ b/src/java.desktop/share/native/liblcms/cmsvirt.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -400,7 +400,7 @@ int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUI
           SumCMY   = (cmsFloat64Number) In[0]  + In[1] + In[2];
           SumCMYK  = SumCMY + In[3];
       
      -    if (SumCMYK > InkLimit) {
      +    if (SumCMYK > InkLimit && SumCMY > 0) {
       
               Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
               if (Ratio < 0)
      @@ -513,16 +513,20 @@ cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIE
           cmsSetColorSpace(hProfile,  cmsSigLabData);
           cmsSetPCS(hProfile,         cmsSigLabData);
       
      -    if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL;
      +    if (!SetTextTags(hProfile, L"Lab identity built-in"))
      +        goto Error;
       
           // An identity LUT is all we need
           LUT = cmsPipelineAlloc(ContextID, 3, 3);
      -    if (LUT == NULL) goto Error;
      +    if (LUT == NULL)
      +        goto Error;
       
           if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3)))
               goto Error;
       
      -    if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error;
      +    if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT))
      +        goto Error;
      +
           cmsPipelineFree(LUT);
       
           return hProfile;
      @@ -550,8 +554,14 @@ cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIE
       {
           cmsHPROFILE hProfile;
           cmsPipeline* LUT = NULL;
      +    cmsCIEXYZ xyz;
       
      -    hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
      +    if (WhitePoint == NULL)
      +        xyz = *cmsD50_XYZ();
      +    else
      +        cmsxyY2XYZ(&xyz, WhitePoint);
      +
      +    hProfile = cmsCreateRGBProfileTHR(ContextID, NULL, NULL, NULL);
           if (hProfile == NULL) return NULL;
       
           cmsSetProfileVersion(hProfile, 4.4);
      @@ -560,6 +570,7 @@ cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIE
           cmsSetColorSpace(hProfile,  cmsSigLabData);
           cmsSetPCS(hProfile,         cmsSigLabData);
       
      +    if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xyz)) goto Error;
           if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error;
       
           // An empty LUTs is all we need
      @@ -929,25 +940,24 @@ cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
       
           for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints;
           CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL);
      -    if (CLUT == NULL) goto Error;
      -
      -
      -    if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) {
      -
      -        // Shouldn't reach here
      +    if (CLUT == NULL)
               goto Error;
      -    }
       
      -    if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
      +    if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0))
      +        goto Error;
      +
      +    if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT))
               goto Error;
      -    }
       
           // Create tags
      -    if (!SetTextTags(hICC, L"BCHS built-in")) return NULL;
      +    if (!SetTextTags(hICC, L"BCHS built-in"))
      +        goto Error;
       
      -    cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ());
      +    if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*)cmsD50_XYZ()))
      +        goto Error;
       
      -    cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline);
      +    if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*)Pipeline))
      +        goto Error;
       
           // Pipeline is already on virtual profile
           cmsPipelineFree(Pipeline);
      diff --git a/src/java.desktop/share/native/liblcms/cmswtpnt.c b/src/java.desktop/share/native/liblcms/cmswtpnt.c
      index ebba2cd6a97..f6337765c0c 100644
      --- a/src/java.desktop/share/native/liblcms/cmswtpnt.c
      +++ b/src/java.desktop/share/native/liblcms/cmswtpnt.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/cmsxform.c b/src/java.desktop/share/native/liblcms/cmsxform.c
      index 1eb3eecbf18..b5dd302b973 100644
      --- a/src/java.desktop/share/native/liblcms/cmsxform.c
      +++ b/src/java.desktop/share/native/liblcms/cmsxform.c
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -1101,6 +1101,8 @@ cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwForm
           int Space1 = (int) T_COLORSPACE(dwFormat);
           int Space2 = _cmsLCMScolorSpace(Check);
       
      +    if (dwFormat == 0) return TRUE; // Bypass used by linkicc
      +
           if (Space1 == PT_ANY) return (T_CHANNELS(dwFormat) == cmsChannelsOf(Check));
           if (Space1 == Space2) return TRUE;
       
      @@ -1183,6 +1185,11 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
               if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
           }
       
      +    if ((dwFlags & cmsFLAGS_GAMUTCHECK) && (nGamutPCSposition <= 0 || nGamutPCSposition >= nProfiles - 1)) {
      +        cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong gamut PCS position '%d'", nGamutPCSposition);
      +        return NULL;
      +    }
      +
           // On floating point transforms, inhibit cache
           if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
               dwFlags |= cmsFLAGS_NOCACHE;
      diff --git a/src/java.desktop/share/native/liblcms/lcms2.h b/src/java.desktop/share/native/liblcms/lcms2.h
      index 5ba09661308..17a52384721 100644
      --- a/src/java.desktop/share/native/liblcms/lcms2.h
      +++ b/src/java.desktop/share/native/liblcms/lcms2.h
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2025 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      @@ -52,7 +52,7 @@
       //
       //---------------------------------------------------------------------------------
       //
      -// Version 2.17
      +// Version 2.18
       //
       
       #ifndef _lcms2_H
      @@ -116,7 +116,7 @@ extern "C" {
       #endif
       
       // Version/release
      -#define LCMS_VERSION        2170
      +#define LCMS_VERSION        2180
       
       // I will give the chance of redefining basic types for compilers that are not fully C99 compliant
       #ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
      diff --git a/src/java.desktop/share/native/liblcms/lcms2_internal.h b/src/java.desktop/share/native/liblcms/lcms2_internal.h
      index d14c0dd823e..6bfe67e5350 100644
      --- a/src/java.desktop/share/native/liblcms/lcms2_internal.h
      +++ b/src/java.desktop/share/native/liblcms/lcms2_internal.h
      @@ -30,7 +30,7 @@
       
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      diff --git a/src/java.desktop/share/native/liblcms/lcms2_plugin.h b/src/java.desktop/share/native/liblcms/lcms2_plugin.h
      index bdfc76f6bf5..85de9bc56d5 100644
      --- a/src/java.desktop/share/native/liblcms/lcms2_plugin.h
      +++ b/src/java.desktop/share/native/liblcms/lcms2_plugin.h
      @@ -30,7 +30,7 @@
       //---------------------------------------------------------------------------------
       //
       //  Little Color Management System
      -//  Copyright (c) 1998-2024 Marti Maria Saguer
      +//  Copyright (c) 1998-2026 Marti Maria Saguer
       //
       // Permission is hereby granted, free of charge, to any person obtaining
       // a copy of this software and associated documentation files (the "Software"),
      
      From 161aa5d52865295059f9506b2ba4ffc4b98324de Mon Sep 17 00:00:00 2001
      From: Mohamed Issa 
      Date: Mon, 9 Feb 2026 19:14:46 +0000
      Subject: [PATCH 182/215] 8371955: Support AVX10 floating point comparison
       instructions
      
      Reviewed-by: epeter, sviswanathan, sparasa
      ---
       src/hotspot/cpu/x86/assembler_x86.cpp         |  40 +-
       src/hotspot/cpu/x86/assembler_x86.hpp         |   6 +-
       src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp |  25 +-
       src/hotspot/cpu/x86/macroAssembler_x86.cpp    |  22 +
       src/hotspot/cpu/x86/macroAssembler_x86.hpp    |  10 +-
       src/hotspot/cpu/x86/x86.ad                    | 387 +++++++++------
       .../compiler/c2/irTests/CMoveLConstants.java  |  35 +-
       .../compiler/lib/ir_framework/IRNode.java     |   5 +
       .../test/ApplicableIRRulesPrinter.java        |   2 +
       .../openjdk/bench/java/lang/FPComparison.java | 444 ++++++++++++++++--
       10 files changed, 774 insertions(+), 202 deletions(-)
      
      diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp
      index cbc5c6988d4..3c8defe62d9 100644
      --- a/src/hotspot/cpu/x86/assembler_x86.cpp
      +++ b/src/hotspot/cpu/x86/assembler_x86.cpp
      @@ -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
      @@ -7320,6 +7320,25 @@ void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
         emit_int16(0x2E, (0xC0 | encode));
       }
       
      +void Assembler::vucomxsd(XMMRegister dst, Address src) {
      +  assert(VM_Version::supports_avx10_2(), "");
      +  InstructionMark im(this);
      +  InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
      +  attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
      +  attributes.set_is_evex_instruction();
      +  vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
      +  emit_int8(0x2E);
      +  emit_operand(dst, src, 0);
      +}
      +
      +void Assembler::vucomxsd(XMMRegister dst, XMMRegister src) {
      +  assert(VM_Version::supports_avx10_2(), "");
      +  InstructionAttr attributes(AVX_128bit, /* rex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
      +  attributes.set_is_evex_instruction();
      +  int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
      +  emit_int16(0x2E, (0xC0 | encode));
      +}
      +
       void Assembler::ucomiss(XMMRegister dst, Address src) {
         InstructionMark im(this);
         InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
      @@ -7335,6 +7354,25 @@ void Assembler::ucomiss(XMMRegister dst, XMMRegister src) {
         emit_int16(0x2E, (0xC0 | encode));
       }
       
      +void Assembler::vucomxss(XMMRegister dst, Address src) {
      +  assert(VM_Version::supports_avx10_2(), "");
      +  InstructionMark im(this);
      +  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
      +  attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
      +  attributes.set_is_evex_instruction();
      +  vex_prefix(src, 0, dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
      +  emit_int8(0x2E);
      +  emit_operand(dst, src, 0);
      +}
      +
      +void Assembler::vucomxss(XMMRegister dst, XMMRegister src) {
      +  assert(VM_Version::supports_avx10_2(), "");
      +  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
      +  attributes.set_is_evex_instruction();
      +  int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
      +  emit_int16(0x2E, (0xC0 | encode));
      +}
      +
       void Assembler::xabort(int8_t imm8) {
         emit_int24((unsigned char)0xC6, (unsigned char)0xF8, (imm8 & 0xFF));
       }
      diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp
      index 26c57fc2d80..97854f712cf 100644
      --- a/src/hotspot/cpu/x86/assembler_x86.hpp
      +++ b/src/hotspot/cpu/x86/assembler_x86.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
      @@ -2331,10 +2331,14 @@ private:
         // Unordered Compare Scalar Double-Precision Floating-Point Values and set EFLAGS
         void ucomisd(XMMRegister dst, Address src);
         void ucomisd(XMMRegister dst, XMMRegister src);
      +  void vucomxsd(XMMRegister dst, Address src);
      +  void vucomxsd(XMMRegister dst, XMMRegister src);
       
         // Unordered Compare Scalar Single-Precision Floating-Point Values and set EFLAGS
         void ucomiss(XMMRegister dst, Address src);
         void ucomiss(XMMRegister dst, XMMRegister src);
      +  void vucomxss(XMMRegister dst, Address src);
      +  void vucomxss(XMMRegister dst, XMMRegister src);
       
         void xabort(int8_t imm8);
       
      diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
      index 8fc3d18abb1..c65b439604b 100644
      --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
      +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.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
      @@ -1046,17 +1046,28 @@ void C2_MacroAssembler::signum_fp(int opcode, XMMRegister dst, XMMRegister zero,
       
         Label DONE_LABEL;
       
      +  // Handle special cases +0.0/-0.0 and NaN, if argument is +0.0/-0.0 or NaN, return argument
      +  // If AVX10.2 (or newer) floating point comparison instructions used, SF=1 for equal and unordered cases
      +  // If other floating point comparison instructions used, ZF=1 for equal and unordered cases
         if (opcode == Op_SignumF) {
      -    ucomiss(dst, zero);
      -    jcc(Assembler::equal, DONE_LABEL);    // handle special case +0.0/-0.0, if argument is +0.0/-0.0, return argument
      -    jcc(Assembler::parity, DONE_LABEL);   // handle special case NaN, if argument NaN, return NaN
      +    if (VM_Version::supports_avx10_2()) {
      +      vucomxss(dst, zero);
      +      jcc(Assembler::negative, DONE_LABEL);
      +    } else {
      +      ucomiss(dst, zero);
      +      jcc(Assembler::equal, DONE_LABEL);
      +    }
           movflt(dst, one);
           jcc(Assembler::above, DONE_LABEL);
           xorps(dst, ExternalAddress(StubRoutines::x86::vector_float_sign_flip()), noreg);
         } else if (opcode == Op_SignumD) {
      -    ucomisd(dst, zero);
      -    jcc(Assembler::equal, DONE_LABEL);    // handle special case +0.0/-0.0, if argument is +0.0/-0.0, return argument
      -    jcc(Assembler::parity, DONE_LABEL);   // handle special case NaN, if argument NaN, return NaN
      +    if (VM_Version::supports_avx10_2()) {
      +      vucomxsd(dst, zero);
      +      jcc(Assembler::negative, DONE_LABEL);
      +    } else {
      +      ucomisd(dst, zero);
      +      jcc(Assembler::equal, DONE_LABEL);
      +    }
           movdbl(dst, one);
           jcc(Assembler::above, DONE_LABEL);
           xorpd(dst, ExternalAddress(StubRoutines::x86::vector_double_sign_flip()), noreg);
      diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
      index b88f510401a..d4d3ec85bcf 100644
      --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
      +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
      @@ -2656,6 +2656,17 @@ void MacroAssembler::ucomisd(XMMRegister dst, AddressLiteral src, Register rscra
         }
       }
       
      +void MacroAssembler::vucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch) {
      +  assert(rscratch != noreg || always_reachable(src), "missing");
      +
      +  if (reachable(src)) {
      +    Assembler::vucomxsd(dst, as_Address(src));
      +  } else {
      +    lea(rscratch, src);
      +    Assembler::vucomxsd(dst, Address(rscratch, 0));
      +  }
      +}
      +
       void MacroAssembler::ucomiss(XMMRegister dst, AddressLiteral src, Register rscratch) {
         assert(rscratch != noreg || always_reachable(src), "missing");
       
      @@ -2667,6 +2678,17 @@ void MacroAssembler::ucomiss(XMMRegister dst, AddressLiteral src, Register rscra
         }
       }
       
      +void MacroAssembler::vucomxss(XMMRegister dst, AddressLiteral src, Register rscratch) {
      +  assert(rscratch != noreg || always_reachable(src), "missing");
      +
      +  if (reachable(src)) {
      +    Assembler::vucomxss(dst, as_Address(src));
      +  } else {
      +    lea(rscratch, src);
      +    Assembler::vucomxss(dst, Address(rscratch, 0));
      +  }
      +}
      +
       void MacroAssembler::xorpd(XMMRegister dst, AddressLiteral src, Register rscratch) {
         assert(rscratch != noreg || always_reachable(src), "missing");
       
      diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.hpp b/src/hotspot/cpu/x86/macroAssembler_x86.hpp
      index 93e3529ac1e..eb23199ca63 100644
      --- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp
      +++ b/src/hotspot/cpu/x86/macroAssembler_x86.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
      @@ -1313,10 +1313,18 @@ public:
         void ucomiss(XMMRegister dst, Address        src) { Assembler::ucomiss(dst, src); }
         void ucomiss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
       
      +  void vucomxss(XMMRegister dst, XMMRegister    src) { Assembler::vucomxss(dst, src); }
      +  void vucomxss(XMMRegister dst, Address        src) { Assembler::vucomxss(dst, src); }
      +  void vucomxss(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
      +
         void ucomisd(XMMRegister dst, XMMRegister    src) { Assembler::ucomisd(dst, src); }
         void ucomisd(XMMRegister dst, Address        src) { Assembler::ucomisd(dst, src); }
         void ucomisd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
       
      +  void vucomxsd(XMMRegister dst, XMMRegister    src) { Assembler::vucomxsd(dst, src); }
      +  void vucomxsd(XMMRegister dst, Address        src) { Assembler::vucomxsd(dst, src); }
      +  void vucomxsd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
      +
         // Bitwise Logical XOR of Packed Double-Precision Floating-Point Values
         void xorpd(XMMRegister dst, XMMRegister    src);
         void xorpd(XMMRegister dst, Address        src) { Assembler::xorpd(dst, src); }
      diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad
      index 93b306c37d6..aed54fe93d4 100644
      --- a/src/hotspot/cpu/x86/x86.ad
      +++ b/src/hotspot/cpu/x86/x86.ad
      @@ -1699,9 +1699,10 @@ static void emit_cmpfp_fixup(MacroAssembler* masm) {
       }
       
       static void emit_cmpfp3(MacroAssembler* masm, Register dst) {
      +  // If any floating point comparison instruction is used, unordered case always triggers jump
      +  // for below condition, CF=1 is true when at least one input is NaN
         Label done;
         __ movl(dst, -1);
      -  __ jcc(Assembler::parity, done);
         __ jcc(Assembler::below, done);
         __ setcc(Assembler::notEqual, dst);
         __ bind(done);
      @@ -5529,12 +5530,21 @@ operand rFlagsRegU()
       operand rFlagsRegUCF() %{
         constraint(ALLOC_IN_RC(int_flags));
         match(RegFlags);
      -  predicate(false);
      +  predicate(!UseAPX || !VM_Version::supports_avx10_2());
       
         format %{ "RFLAGS_U_CF" %}
         interface(REG_INTER);
       %}
       
      +operand rFlagsRegUCFE() %{
      +  constraint(ALLOC_IN_RC(int_flags));
      +  match(RegFlags);
      +  predicate(UseAPX && VM_Version::supports_avx10_2());
      +
      +  format %{ "RFLAGS_U_CFE" %}
      +  interface(REG_INTER);
      +%}
      +
       // Float register operands
       operand regF() %{
          constraint(ALLOC_IN_RC(float_reg));
      @@ -6027,10 +6037,10 @@ operand cmpOp()
         interface(COND_INTER) %{
           equal(0x4, "e");
           not_equal(0x5, "ne");
      -    less(0xC, "l");
      -    greater_equal(0xD, "ge");
      -    less_equal(0xE, "le");
      -    greater(0xF, "g");
      +    less(0xc, "l");
      +    greater_equal(0xd, "ge");
      +    less_equal(0xe, "le");
      +    greater(0xf, "g");
           overflow(0x0, "o");
           no_overflow(0x1, "no");
         %}
      @@ -6062,11 +6072,12 @@ operand cmpOpU()
       // don't need to use cmpOpUCF2 for eq/ne
       operand cmpOpUCF() %{
         match(Bool);
      -  predicate(n->as_Bool()->_test._test == BoolTest::lt ||
      -            n->as_Bool()->_test._test == BoolTest::ge ||
      -            n->as_Bool()->_test._test == BoolTest::le ||
      -            n->as_Bool()->_test._test == BoolTest::gt ||
      -            n->in(1)->in(1) == n->in(1)->in(2));
      +  predicate((!UseAPX || !VM_Version::supports_avx10_2()) &&
      +            (n->as_Bool()->_test._test == BoolTest::lt ||
      +             n->as_Bool()->_test._test == BoolTest::ge ||
      +             n->as_Bool()->_test._test == BoolTest::le ||
      +             n->as_Bool()->_test._test == BoolTest::gt ||
      +             n->in(1)->in(1) == n->in(1)->in(2)));
         format %{ "" %}
         interface(COND_INTER) %{
           equal(0xb, "np");
      @@ -6084,7 +6095,8 @@ operand cmpOpUCF() %{
       // Floating comparisons that can be fixed up with extra conditional jumps
       operand cmpOpUCF2() %{
         match(Bool);
      -  predicate((n->as_Bool()->_test._test == BoolTest::ne ||
      +  predicate((!UseAPX || !VM_Version::supports_avx10_2()) &&
      +            (n->as_Bool()->_test._test == BoolTest::ne ||
                    n->as_Bool()->_test._test == BoolTest::eq) &&
                   n->in(1)->in(1) != n->in(1)->in(2));
         format %{ "" %}
      @@ -6100,6 +6112,37 @@ operand cmpOpUCF2() %{
         %}
       %}
       
      +
      +// Floating point comparisons that set condition flags to test more directly,
      +// Unsigned tests are used for G (>) and GE (>=) conditions while signed tests
      +// are used for L (<) and LE (<=) conditions. It's important to convert these
      +// latter conditions to ones that use unsigned tests before passing into an
      +// instruction because the preceding comparison might be based on a three way
      +// comparison (CmpF3 or CmpD3) that also assigns unordered outcomes to -1.
      +operand cmpOpUCFE()
      +%{
      +  match(Bool);
      +  predicate((UseAPX && VM_Version::supports_avx10_2()) &&
      +            (n->as_Bool()->_test._test == BoolTest::ne ||
      +             n->as_Bool()->_test._test == BoolTest::eq ||
      +             n->as_Bool()->_test._test == BoolTest::lt ||
      +             n->as_Bool()->_test._test == BoolTest::ge ||
      +             n->as_Bool()->_test._test == BoolTest::le ||
      +             n->as_Bool()->_test._test == BoolTest::gt));
      +
      +  format %{ "" %}
      +  interface(COND_INTER) %{
      +    equal(0x4, "e");
      +    not_equal(0x5, "ne");
      +    less(0x2, "b");
      +    greater_equal(0x3, "ae");
      +    less_equal(0x6, "be");
      +    greater(0x7, "a");
      +    overflow(0x0, "o");
      +    no_overflow(0x1, "no");
      +  %}
      +%}
      +
       // Operands for bound floating pointer register arguments
       operand rxmm0() %{
         constraint(ALLOC_IN_RC(xmm0_reg));
      @@ -9116,20 +9159,34 @@ instruct cmovI_imm_01UCF(rRegI dst, immI_1 src, rFlagsRegUCF cr, cmpOpUCF cop)
         ins_pipe(ialu_reg);
       %}
       
      +instruct cmovI_imm_01UCFE(rRegI dst, immI_1 src, rFlagsRegUCFE cr, cmpOpUCFE cop)
      +%{
      +  predicate(n->in(2)->in(2)->is_Con() && n->in(2)->in(2)->get_int() == 0);
      +  match(Set dst (CMoveI (Binary cop cr) (Binary src dst)));
      +
      +  ins_cost(100); // XXX
      +  format %{ "setbn$cop $dst\t# signed, unsigned, int" %}
      +  ins_encode %{
      +    Assembler::Condition cond = (Assembler::Condition)($cop$$cmpcode);
      +    __ setb(MacroAssembler::negate_condition(cond), $dst$$Register);
      +  %}
      +  ins_pipe(ialu_reg);
      +%}
      +
       instruct cmovI_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
      -  predicate(!UseAPX);
         match(Set dst (CMoveI (Binary cop cr) (Binary dst src)));
      +
         ins_cost(200);
         expand %{
           cmovI_regU(cop, cr, dst, src);
         %}
       %}
       
      -instruct cmovI_regUCF_ndd(rRegI dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegI src1, rRegI src2) %{
      -  predicate(UseAPX);
      +instruct cmovI_regUCFE_ndd(rRegI dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegI src1, rRegI src2) %{
         match(Set dst (CMoveI (Binary cop cr) (Binary src1 src2)));
      +
         ins_cost(200);
      -  format %{ "ecmovl$cop $dst, $src1, $src2\t# unsigned, int ndd" %}
      +  format %{ "ecmovl$cop $dst, $src1, $src2\t# signed, unsigned, int ndd" %}
         ins_encode %{
           __ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Register);
         %}
      @@ -9137,7 +9194,7 @@ instruct cmovI_regUCF_ndd(rRegI dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegI src1,
       %}
       
       instruct cmovI_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
      -  predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
      +  predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
         match(Set dst (CMoveI (Binary cop cr) (Binary dst src)));
       
         ins_cost(200); // XXX
      @@ -9150,25 +9207,10 @@ instruct cmovI_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src)
         ins_pipe(pipe_cmov_reg);
       %}
       
      -instruct cmovI_regUCF2_ne_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src1, rRegI src2) %{
      -  predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
      -  match(Set dst (CMoveI (Binary cop cr) (Binary src1 src2)));
      -  effect(TEMP dst);
      -
      -  ins_cost(200);
      -  format %{ "ecmovpl  $dst, $src1, $src2\n\t"
      -            "cmovnel  $dst, $src2" %}
      -  ins_encode %{
      -    __ ecmovl(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
      -    __ cmovl(Assembler::notEqual, $dst$$Register, $src2$$Register);
      -  %}
      -  ins_pipe(pipe_cmov_reg);
      -%}
      -
       // Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
       // inputs of the CMove
       instruct cmovI_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src) %{
      -  predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
      +  predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
         match(Set dst (CMoveI (Binary cop cr) (Binary src dst)));
         effect(TEMP dst);
       
      @@ -9182,23 +9224,6 @@ instruct cmovI_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src)
         ins_pipe(pipe_cmov_reg);
       %}
       
      -// We need this special handling for only eq / neq comparison since NaN == NaN is false,
      -// and parity flag bit is set if any of the operand is a NaN.
      -instruct cmovI_regUCF2_eq_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegI dst, rRegI src1, rRegI src2) %{
      -  predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
      -  match(Set dst (CMoveI (Binary cop cr) (Binary src2 src1)));
      -  effect(TEMP dst);
      -
      -  ins_cost(200);
      -  format %{ "ecmovpl  $dst, $src1, $src2\n\t"
      -            "cmovnel  $dst, $src2" %}
      -  ins_encode %{
      -    __ ecmovl(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
      -    __ cmovl(Assembler::notEqual, $dst$$Register, $src2$$Register);
      -  %}
      -  ins_pipe(pipe_cmov_reg);
      -%}
      -
       // Conditional move
       instruct cmovI_mem(cmpOp cop, rFlagsReg cr, rRegI dst, memory src) %{
         predicate(!UseAPX);
      @@ -9241,8 +9266,8 @@ instruct cmovI_memU(cmpOpU cop, rFlagsRegU cr, rRegI dst, memory src)
       %}
       
       instruct cmovI_memUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegI dst, memory src) %{
      -  predicate(!UseAPX);
         match(Set dst (CMoveI (Binary cop cr) (Binary dst (LoadI src))));
      +
         ins_cost(250);
         expand %{
           cmovI_memU(cop, cr, dst, src);
      @@ -9262,12 +9287,12 @@ instruct cmovI_rReg_rReg_memU_ndd(rRegI dst, cmpOpU cop, rFlagsRegU cr, rRegI sr
         ins_pipe(pipe_cmov_mem);
       %}
       
      -instruct cmovI_rReg_rReg_memUCF_ndd(rRegI dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegI src1, memory src2)
      +instruct cmovI_rReg_rReg_memUCFE_ndd(rRegI dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegI src1, memory src2)
       %{
      -  predicate(UseAPX);
         match(Set dst (CMoveI (Binary cop cr) (Binary src1 (LoadI src2))));
      +
         ins_cost(250);
      -  format %{ "ecmovl$cop $dst, $src1, $src2\t# unsigned, int ndd" %}
      +  format %{ "ecmovl$cop $dst, $src1, $src2\t# signed, unsigned, int ndd" %}
         ins_encode %{
           __ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address);
         %}
      @@ -9317,8 +9342,8 @@ instruct cmovN_regU(cmpOpU cop, rFlagsRegU cr, rRegN dst, rRegN src)
       %}
       
       instruct cmovN_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegN dst, rRegN src) %{
      -  predicate(!UseAPX);
         match(Set dst (CMoveN (Binary cop cr) (Binary dst src)));
      +
         ins_cost(200);
         expand %{
           cmovN_regU(cop, cr, dst, src);
      @@ -9339,11 +9364,11 @@ instruct cmovN_regU_ndd(rRegN dst, cmpOpU cop, rFlagsRegU cr, rRegN src1, rRegN
         ins_pipe(pipe_cmov_reg);
       %}
       
      -instruct cmovN_regUCF_ndd(rRegN dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegN src1, rRegN src2) %{
      -  predicate(UseAPX);
      +instruct cmovN_regUCFE_ndd(rRegN dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegN src1, rRegN src2) %{
         match(Set dst (CMoveN (Binary cop cr) (Binary src1 src2)));
      +
         ins_cost(200);
      -  format %{ "ecmovl$cop $dst, $src1, $src2\t# unsigned, compressed ptr ndd" %}
      +  format %{ "ecmovl$cop $dst, $src1, $src2\t# signed, unsigned, compressed ptr ndd" %}
         ins_encode %{
           __ ecmovl((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Register);
         %}
      @@ -9437,19 +9462,19 @@ instruct cmovP_regU_ndd(rRegP dst, cmpOpU cop, rFlagsRegU cr, rRegP src1, rRegP
       %}
       
       instruct cmovP_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
      -  predicate(!UseAPX);
         match(Set dst (CMoveP (Binary cop cr) (Binary dst src)));
      +
         ins_cost(200);
         expand %{
           cmovP_regU(cop, cr, dst, src);
         %}
       %}
       
      -instruct cmovP_regUCF_ndd(rRegP dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegP src1, rRegP src2) %{
      -  predicate(UseAPX);
      +instruct cmovP_regUCFE_ndd(rRegP dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegP src1, rRegP src2) %{
         match(Set dst (CMoveP (Binary cop cr) (Binary src1 src2)));
      +
         ins_cost(200);
      -  format %{ "ecmovq$cop $dst, $src1, $src2\t# unsigned, ptr ndd" %}
      +  format %{ "ecmovq$cop $dst, $src1, $src2\t# signed, unsigned, ptr ndd" %}
         ins_encode %{
           __ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Register);
         %}
      @@ -9457,7 +9482,7 @@ instruct cmovP_regUCF_ndd(rRegP dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegP src1,
       %}
       
       instruct cmovP_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
      -  predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
      +  predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
         match(Set dst (CMoveP (Binary cop cr) (Binary dst src)));
       
         ins_cost(200); // XXX
      @@ -9470,25 +9495,10 @@ instruct cmovP_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src)
         ins_pipe(pipe_cmov_reg);
       %}
       
      -instruct cmovP_regUCF2_ne_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src1, rRegP src2) %{
      -  predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
      -  match(Set dst (CMoveP (Binary cop cr) (Binary src1 src2)));
      -  effect(TEMP dst);
      -
      -  ins_cost(200);
      -  format %{ "ecmovpq  $dst, $src1, $src2\n\t"
      -            "cmovneq  $dst, $src2" %}
      -  ins_encode %{
      -    __ ecmovq(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
      -    __ cmovq(Assembler::notEqual, $dst$$Register, $src2$$Register);
      -  %}
      -  ins_pipe(pipe_cmov_reg);
      -%}
      -
       // Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
       // inputs of the CMove
       instruct cmovP_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src) %{
      -  predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
      +  predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
         match(Set dst (CMoveP (Binary cop cr) (Binary src dst)));
       
         ins_cost(200); // XXX
      @@ -9501,21 +9511,6 @@ instruct cmovP_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src)
         ins_pipe(pipe_cmov_reg);
       %}
       
      -instruct cmovP_regUCF2_eq_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegP dst, rRegP src1, rRegP src2) %{
      -  predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
      -  match(Set dst (CMoveP (Binary cop cr) (Binary src2 src1)));
      -  effect(TEMP dst);
      -
      -  ins_cost(200);
      -  format %{ "ecmovpq  $dst, $src1, $src2\n\t"
      -            "cmovneq  $dst, $src2" %}
      -  ins_encode %{
      -    __ ecmovq(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
      -    __ cmovq(Assembler::notEqual, $dst$$Register, $src2$$Register);
      -  %}
      -  ins_pipe(pipe_cmov_reg);
      -%}
      -
       instruct cmovL_imm_01(rRegL dst, immL1 src, rFlagsReg cr, cmpOp cop)
       %{
         predicate(n->in(2)->in(2)->is_Con() && n->in(2)->in(2)->get_long() == 0);
      @@ -9636,21 +9631,35 @@ instruct cmovL_imm_01UCF(rRegL dst, immL1 src, rFlagsRegUCF cr, cmpOpUCF cop)
         ins_pipe(ialu_reg);
       %}
       
      +instruct cmovL_imm_01UCFE(rRegL dst, immL1 src, rFlagsRegUCFE cr, cmpOpUCFE cop)
      +%{
      +  predicate(n->in(2)->in(2)->is_Con() && n->in(2)->in(2)->get_long() == 0);
      +  match(Set dst (CMoveL (Binary cop cr) (Binary src dst)));
      +
      +  ins_cost(100); // XXX
      +  format %{ "setbn$cop $dst\t# signed, unsigned, long" %}
      +  ins_encode %{
      +    Assembler::Condition cond = (Assembler::Condition)($cop$$cmpcode);
      +    __ setb(MacroAssembler::negate_condition(cond), $dst$$Register);
      +  %}
      +  ins_pipe(ialu_reg);
      +%}
      +
       instruct cmovL_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
      -  predicate(!UseAPX);
         match(Set dst (CMoveL (Binary cop cr) (Binary dst src)));
      +
         ins_cost(200);
         expand %{
           cmovL_regU(cop, cr, dst, src);
         %}
       %}
       
      -instruct cmovL_regUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1, rRegL src2)
      +instruct cmovL_regUCFE_ndd(rRegL dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegL src1, rRegL src2)
       %{
      -  predicate(UseAPX);
         match(Set dst (CMoveL (Binary cop cr) (Binary src1 src2)));
      +
         ins_cost(200);
      -  format %{ "ecmovq$cop $dst, $src1, $src2\t# unsigned, long ndd" %}
      +  format %{ "ecmovq$cop $dst, $src1, $src2\t# signed, unsigned, long ndd" %}
         ins_encode %{
           __ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Register);
         %}
      @@ -9658,7 +9667,7 @@ instruct cmovL_regUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1,
       %}
       
       instruct cmovL_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
      -  predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
      +  predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
         match(Set dst (CMoveL (Binary cop cr) (Binary dst src)));
       
         ins_cost(200); // XXX
      @@ -9671,25 +9680,10 @@ instruct cmovL_regUCF2_ne(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src)
         ins_pipe(pipe_cmov_reg);
       %}
       
      -instruct cmovL_regUCF2_ne_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src1, rRegL src2) %{
      -  predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::ne);
      -  match(Set dst (CMoveL (Binary cop cr) (Binary src1 src2)));
      -  effect(TEMP dst);
      -
      -  ins_cost(200);
      -  format %{ "ecmovpq  $dst, $src1, $src2\n\t"
      -            "cmovneq  $dst, $src2" %}
      -  ins_encode %{
      -    __ ecmovq(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
      -    __ cmovq(Assembler::notEqual, $dst$$Register, $src2$$Register);
      -  %}
      -  ins_pipe(pipe_cmov_reg);
      -%}
      -
       // Since (x == y) == !(x != y), we can flip the sense of the test by flipping the
       // inputs of the CMove
       instruct cmovL_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src) %{
      -  predicate(!UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
      +  predicate(n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
         match(Set dst (CMoveL (Binary cop cr) (Binary src dst)));
       
         ins_cost(200); // XXX
      @@ -9702,21 +9696,6 @@ instruct cmovL_regUCF2_eq(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src)
         ins_pipe(pipe_cmov_reg);
       %}
       
      -instruct cmovL_regUCF2_eq_ndd(cmpOpUCF2 cop, rFlagsRegUCF cr, rRegL dst, rRegL src1, rRegL src2) %{
      -  predicate(UseAPX && n->in(1)->in(1)->as_Bool()->_test._test == BoolTest::eq);
      -  match(Set dst (CMoveL (Binary cop cr) (Binary src2 src1)));
      -  effect(TEMP dst);
      -
      -  ins_cost(200);
      -  format %{ "ecmovpq  $dst, $src1, $src2\n\t"
      -            "cmovneq $dst, $src2" %}
      -  ins_encode %{
      -    __ ecmovq(Assembler::parity, $dst$$Register, $src1$$Register, $src2$$Register);
      -    __ cmovq(Assembler::notEqual, $dst$$Register, $src2$$Register);
      -  %}
      -  ins_pipe(pipe_cmov_reg);
      -%}
      -
       instruct cmovL_memU(cmpOpU cop, rFlagsRegU cr, rRegL dst, memory src)
       %{
         predicate(!UseAPX);
      @@ -9731,8 +9710,8 @@ instruct cmovL_memU(cmpOpU cop, rFlagsRegU cr, rRegL dst, memory src)
       %}
       
       instruct cmovL_memUCF(cmpOpUCF cop, rFlagsRegUCF cr, rRegL dst, memory src) %{
      -  predicate(!UseAPX);
         match(Set dst (CMoveL (Binary cop cr) (Binary dst (LoadL src))));
      +
         ins_cost(200);
         expand %{
           cmovL_memU(cop, cr, dst, src);
      @@ -9752,12 +9731,12 @@ instruct cmovL_rReg_rReg_memU_ndd(rRegL dst, cmpOpU cop, rFlagsRegU cr, rRegL sr
         ins_pipe(pipe_cmov_mem);
       %}
       
      -instruct cmovL_rReg_rReg_memUCF_ndd(rRegL dst, cmpOpUCF cop, rFlagsRegUCF cr, rRegL src1, memory src2)
      +instruct cmovL_rReg_rReg_memUCFE_ndd(rRegL dst, cmpOpUCFE cop, rFlagsRegUCFE cr, rRegL src1, memory src2)
       %{
      -  predicate(UseAPX);
         match(Set dst (CMoveL (Binary cop cr) (Binary src1 (LoadL src2))));
      +
         ins_cost(200);
      -  format %{ "ecmovq$cop $dst, $src1, $src2\t# unsigned, long ndd" %}
      +  format %{ "ecmovq$cop $dst, $src1, $src2\t# signed, unsigned, long ndd" %}
         ins_encode %{
           __ ecmovq((Assembler::Condition)($cop$$cmpcode), $dst$$Register, $src1$$Register, $src2$$Address);
         %}
      @@ -9802,12 +9781,31 @@ instruct cmovF_regU(cmpOpU cop, rFlagsRegU cr, regF dst, regF src)
       
       instruct cmovF_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, regF dst, regF src) %{
         match(Set dst (CMoveF (Binary cop cr) (Binary dst src)));
      +
         ins_cost(200);
         expand %{
           cmovF_regU(cop, cr, dst, src);
         %}
       %}
       
      +instruct cmovF_regUCFE(cmpOpUCFE cop, rFlagsRegUCFE cr, regF dst, regF src)
      +%{
      +  match(Set dst (CMoveF (Binary cop cr) (Binary dst src)));
      +
      +  ins_cost(200); // XXX
      +  format %{ "jn$cop    skip\t# signed, unsigned cmove float\n\t"
      +            "movss     $dst, $src\n"
      +    "skip:" %}
      +  ins_encode %{
      +    Label Lskip;
      +    // Invert sense of branch from sense of CMOV
      +    __ jccb((Assembler::Condition)($cop$$cmpcode^1), Lskip);
      +    __ movflt($dst$$XMMRegister, $src$$XMMRegister);
      +    __ bind(Lskip);
      +  %}
      +  ins_pipe(pipe_slow);
      +%}
      +
       instruct cmovD_reg(cmpOp cop, rFlagsReg cr, regD dst, regD src)
       %{
         match(Set dst (CMoveD (Binary cop cr) (Binary dst src)));
      @@ -9846,12 +9844,31 @@ instruct cmovD_regU(cmpOpU cop, rFlagsRegU cr, regD dst, regD src)
       
       instruct cmovD_regUCF(cmpOpUCF cop, rFlagsRegUCF cr, regD dst, regD src) %{
         match(Set dst (CMoveD (Binary cop cr) (Binary dst src)));
      +
         ins_cost(200);
         expand %{
           cmovD_regU(cop, cr, dst, src);
         %}
       %}
       
      +instruct cmovD_regUCFE(cmpOpUCFE cop, rFlagsRegUCFE cr, regD dst, regD src)
      +%{
      +  match(Set dst (CMoveD (Binary cop cr) (Binary dst src)));
      +
      +  ins_cost(200); // XXX
      +  format %{ "jn$cop    skip\t# signed, unsigned cmove double\n\t"
      +            "movsd     $dst, $src\n"
      +    "skip:" %}
      +  ins_encode %{
      +    Label Lskip;
      +    // Invert sense of branch from sense of CMOV
      +    __ jccb((Assembler::Condition)($cop$$cmpcode^1), Lskip);
      +    __ movdbl($dst$$XMMRegister, $src$$XMMRegister);
      +    __ bind(Lskip);
      +  %}
      +  ins_pipe(pipe_slow);
      +%}
      +
       //----------Arithmetic Instructions--------------------------------------------
       //----------Addition Instructions----------------------------------------------
       
      @@ -14319,7 +14336,7 @@ instruct cmpF_cc_reg(rFlagsRegU cr, regF src1, regF src2)
         ins_pipe(pipe_slow);
       %}
       
      -instruct cmpF_cc_reg_CF(rFlagsRegUCF cr, regF src1, regF src2) %{
      +instruct cmpF_cc_regCF(rFlagsRegUCF cr, regF src1, regF src2) %{
         match(Set cr (CmpF src1 src2));
       
         ins_cost(100);
      @@ -14330,6 +14347,17 @@ instruct cmpF_cc_reg_CF(rFlagsRegUCF cr, regF src1, regF src2) %{
         ins_pipe(pipe_slow);
       %}
       
      +instruct cmpF_cc_regCFE(rFlagsRegUCFE cr, regF src1, regF src2) %{
      +  match(Set cr (CmpF src1 src2));
      +
      +  ins_cost(100);
      +  format %{ "vucomxss $src1, $src2" %}
      +  ins_encode %{
      +    __ vucomxss($src1$$XMMRegister, $src2$$XMMRegister);
      +  %}
      +  ins_pipe(pipe_slow);
      +%}
      +
       instruct cmpF_cc_memCF(rFlagsRegUCF cr, regF src1, memory src2) %{
         match(Set cr (CmpF src1 (LoadF src2)));
       
      @@ -14341,8 +14369,20 @@ instruct cmpF_cc_memCF(rFlagsRegUCF cr, regF src1, memory src2) %{
         ins_pipe(pipe_slow);
       %}
       
      +instruct cmpF_cc_memCFE(rFlagsRegUCFE cr, regF src1, memory src2) %{
      +  match(Set cr (CmpF src1 (LoadF src2)));
      +
      +  ins_cost(100);
      +  format %{ "vucomxss $src1, $src2" %}
      +  ins_encode %{
      +    __ vucomxss($src1$$XMMRegister, $src2$$Address);
      +  %}
      +  ins_pipe(pipe_slow);
      +%}
      +
       instruct cmpF_cc_immCF(rFlagsRegUCF cr, regF src, immF con) %{
         match(Set cr (CmpF src con));
      +
         ins_cost(100);
         format %{ "ucomiss $src, [$constantaddress]\t# load from constant table: float=$con" %}
         ins_encode %{
      @@ -14351,6 +14391,17 @@ instruct cmpF_cc_immCF(rFlagsRegUCF cr, regF src, immF con) %{
         ins_pipe(pipe_slow);
       %}
       
      +instruct cmpF_cc_immCFE(rFlagsRegUCFE cr, regF src, immF con) %{
      +  match(Set cr (CmpF src con));
      +
      +  ins_cost(100);
      +  format %{ "vucomxss $src, [$constantaddress]\t# load from constant table: float=$con" %}
      +  ins_encode %{
      +    __ vucomxss($src$$XMMRegister, $constantaddress($con));
      +  %}
      +  ins_pipe(pipe_slow);
      +%}
      +
       // Really expensive, avoid
       instruct cmpD_cc_reg(rFlagsRegU cr, regD src1, regD src2)
       %{
      @@ -14370,7 +14421,7 @@ instruct cmpD_cc_reg(rFlagsRegU cr, regD src1, regD src2)
         ins_pipe(pipe_slow);
       %}
       
      -instruct cmpD_cc_reg_CF(rFlagsRegUCF cr, regD src1, regD src2) %{
      +instruct cmpD_cc_regCF(rFlagsRegUCF cr, regD src1, regD src2) %{
         match(Set cr (CmpD src1 src2));
       
         ins_cost(100);
      @@ -14381,6 +14432,17 @@ instruct cmpD_cc_reg_CF(rFlagsRegUCF cr, regD src1, regD src2) %{
         ins_pipe(pipe_slow);
       %}
       
      +instruct cmpD_cc_regCFE(rFlagsRegUCFE cr, regD src1, regD src2) %{
      +  match(Set cr (CmpD src1 src2));
      +
      +  ins_cost(100);
      +  format %{ "vucomxsd $src1, $src2 test" %}
      +  ins_encode %{
      +    __ vucomxsd($src1$$XMMRegister, $src2$$XMMRegister);
      +  %}
      +  ins_pipe(pipe_slow);
      +%}
      +
       instruct cmpD_cc_memCF(rFlagsRegUCF cr, regD src1, memory src2) %{
         match(Set cr (CmpD src1 (LoadD src2)));
       
      @@ -14392,6 +14454,17 @@ instruct cmpD_cc_memCF(rFlagsRegUCF cr, regD src1, memory src2) %{
         ins_pipe(pipe_slow);
       %}
       
      +instruct cmpD_cc_memCFE(rFlagsRegUCFE cr, regD src1, memory src2) %{
      +  match(Set cr (CmpD src1 (LoadD src2)));
      +
      +  ins_cost(100);
      +  format %{ "vucomxsd $src1, $src2" %}
      +  ins_encode %{
      +    __ vucomxsd($src1$$XMMRegister, $src2$$Address);
      +  %}
      +  ins_pipe(pipe_slow);
      +%}
      +
       instruct cmpD_cc_immCF(rFlagsRegUCF cr, regD src, immD con) %{
         match(Set cr (CmpD src con));
         ins_cost(100);
      @@ -14402,6 +14475,17 @@ instruct cmpD_cc_immCF(rFlagsRegUCF cr, regD src, immD con) %{
         ins_pipe(pipe_slow);
       %}
       
      +instruct cmpD_cc_immCFE(rFlagsRegUCFE cr, regD src, immD con) %{
      +  match(Set cr (CmpD src con));
      +
      +  ins_cost(100);
      +  format %{ "vucomxsd $src, [$constantaddress]\t# load from constant table: double=$con" %}
      +  ins_encode %{
      +    __ vucomxsd($src$$XMMRegister, $constantaddress($con));
      +  %}
      +  ins_pipe(pipe_slow);
      +%}
      +
       // Compare into -1,0,1
       instruct cmpF_reg(rRegI dst, regF src1, regF src2, rFlagsReg cr)
       %{
      @@ -16808,6 +16892,21 @@ instruct jmpConUCF2(cmpOpUCF2 cop, rFlagsRegUCF cmp, label labl) %{
         ins_pipe(pipe_jcc);
       %}
       
      +// Jump Direct Conditional - using signed and unsigned comparison
      +instruct jmpConUCFE(cmpOpUCFE cop, rFlagsRegUCFE cmp, label labl) %{
      +  match(If cop cmp);
      +  effect(USE labl);
      +
      +  ins_cost(200);
      +  format %{ "j$cop,su   $labl" %}
      +  size(6);
      +  ins_encode %{
      +    Label* L = $labl$$label;
      +    __ jcc((Assembler::Condition)($cop$$cmpcode), *L, false); // Always long jump
      +  %}
      +  ins_pipe(pipe_jcc);
      +%}
      +
       // ============================================================================
       // The 2nd slow-half of a subtype check.  Scan the subklass's 2ndary
       // superklass array for an instance of the superklass.  Set a hidden
      @@ -17026,6 +17125,22 @@ instruct jmpConUCF2_short(cmpOpUCF2 cop, rFlagsRegUCF cmp, label labl) %{
         ins_short_branch(1);
       %}
       
      +// Jump Direct Conditional - using signed and unsigned comparison
      +instruct jmpConUCFE_short(cmpOpUCFE cop, rFlagsRegUCFE cmp, label labl) %{
      +  match(If cop cmp);
      +  effect(USE labl);
      +
      +  ins_cost(300);
      +  format %{ "j$cop,sus  $labl" %}
      +  size(2);
      +  ins_encode %{
      +    Label* L = $labl$$label;
      +    __ jccb((Assembler::Condition)($cop$$cmpcode), *L);
      +  %}
      +  ins_pipe(pipe_jcc);
      +  ins_short_branch(1);
      +%}
      +
       // ============================================================================
       // inlined locking and unlocking
       
      diff --git a/test/hotspot/jtreg/compiler/c2/irTests/CMoveLConstants.java b/test/hotspot/jtreg/compiler/c2/irTests/CMoveLConstants.java
      index e62e1adc8d0..cf86764aecc 100644
      --- a/test/hotspot/jtreg/compiler/c2/irTests/CMoveLConstants.java
      +++ b/test/hotspot/jtreg/compiler/c2/irTests/CMoveLConstants.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
      @@ -39,31 +39,56 @@ public class CMoveLConstants {
           }
       
           @Test
      -    @IR(applyIfPlatform = {"x64", "true"}, counts = {IRNode.X86_CMOVEL_IMM01, "1"}, phase = CompilePhase.FINAL_CODE)
      +    @IR(counts = {IRNode.X86_CMOVEL_IMM01, "1"},
      +        applyIfPlatform = {"x64", "true"},
      +        phase = CompilePhase.FINAL_CODE)
           public static long testSigned(int a, int b) {
               return a > b ? 1L : 0L;
           }
       
           @Test
      -    @IR(applyIfPlatform = {"x64", "true"}, counts = {IRNode.X86_CMOVEL_IMM01U, "1"}, phase = CompilePhase.FINAL_CODE)
      +    @IR(counts = {IRNode.X86_CMOVEL_IMM01U, "1"},
      +        applyIfPlatform = {"x64", "true"},
      +        phase = CompilePhase.FINAL_CODE)
           public static long testUnsigned(int a, int b) {
               return Integer.compareUnsigned(a, b) > 0 ? 1L : 0L;
           }
       
           @Test
      -    @IR(applyIfPlatform = {"x64", "true"}, counts = {IRNode.X86_CMOVEL_IMM01UCF, "1"}, phase = CompilePhase.FINAL_CODE)
      +    @IR(counts = {IRNode.X86_CMOVEL_IMM01UCF, "1"},
      +        applyIfPlatform = {"x64", "true"},
      +        applyIfCPUFeatureOr = {"apx_f", "false", "avx10_2", "false"},
      +        phase = CompilePhase.FINAL_CODE)
      +    @IR(counts = {IRNode.X86_CMOVEL_IMM01UCFE, "1"},
      +        applyIfPlatform = {"x64", "true"},
      +        applyIfCPUFeatureAnd = {"apx_f", "true", "avx10_2", "true"},
      +        phase = CompilePhase.FINAL_CODE)
           public static long testFloat(float a, float b) {
               return a > b ? 1L : 0L;
           }
       
      +    @Test
      +    @IR(counts = {IRNode.X86_CMOVEL_IMM01UCF, "1"},
      +        applyIfPlatform = {"x64", "true"},
      +        applyIfCPUFeatureOr = {"apx_f", "false", "avx10_2", "false"},
      +        phase = CompilePhase.FINAL_CODE)
      +    @IR(counts = {IRNode.X86_CMOVEL_IMM01UCFE, "1"},
      +        applyIfPlatform = {"x64", "true"},
      +        applyIfCPUFeatureAnd = {"apx_f", "true", "avx10_2", "true"},
      +        phase = CompilePhase.FINAL_CODE)
      +    public static long testDouble(double a, double b) {
      +        return a > b ? 1L : 0L;
      +    }
      +
           @DontCompile
           public void assertResults(int a, int b) {
               Asserts.assertEQ(a > b ? 1L : 0L, testSigned(a, b));
               Asserts.assertEQ(Integer.compareUnsigned(a, b) > 0 ? 1L : 0L, testUnsigned(a, b));
               Asserts.assertEQ((float) a > (float) b ? 1L : 0L, testFloat(a, b));
      +        Asserts.assertEQ((double) a > (double) b ? 1L : 0L, testDouble(a, b));
           }
       
      -    @Run(test = {"testSigned", "testUnsigned", "testFloat"})
      +    @Run(test = {"testSigned", "testUnsigned", "testFloat", "testDouble"})
           public void runMethod() {
               assertResults(10, 20);
               assertResults(20, 10);
      diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
      index 43cae5aa6c7..f488e930710 100644
      --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
      +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
      @@ -3090,6 +3090,11 @@ public class IRNode {
               machOnlyNameRegex(X86_CMOVEL_IMM01UCF, "cmovL_imm_01UCF");
           }
       
      +    public static final String X86_CMOVEL_IMM01UCFE = PREFIX + "X86_CMOVEL_IMM01UCFE" + POSTFIX;
      +    static {
      +        machOnlyNameRegex(X86_CMOVEL_IMM01UCFE, "cmovL_imm_01UCFE");
      +    }
      +
           public static final String MOD_F = PREFIX + "MOD_F" + POSTFIX;
           static {
               String regex = START + "ModF" + MID + END;
      diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
      index 4fa1f8f3fe5..7a868c172dd 100644
      --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
      +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
      @@ -110,6 +110,8 @@ public class ApplicableIRRulesPrinter {
               "avx512_vbmi2",
               "avx10_2",
               "bmi2",
      +        // Intel APX
      +        "apx_f",
               // AArch64
               "sha3",
               "asimd",
      diff --git a/test/micro/org/openjdk/bench/java/lang/FPComparison.java b/test/micro/org/openjdk/bench/java/lang/FPComparison.java
      index e6b2ab2d39a..63cbb6db911 100644
      --- a/test/micro/org/openjdk/bench/java/lang/FPComparison.java
      +++ b/test/micro/org/openjdk/bench/java/lang/FPComparison.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
      @@ -32,7 +32,7 @@ import java.util.random.RandomGenerator;
       @OutputTimeUnit(TimeUnit.NANOSECONDS)
       @State(Scope.Thread)
       @Warmup(iterations = 5, time = 1)
      -@Measurement(iterations = 5, time = 1)
      +@Measurement(iterations = 5, time = 5)
       @Fork(3)
       public class FPComparison {
           static final int INVOCATIONS = 1024;
      @@ -75,331 +75,673 @@ public class FPComparison {
               }
           }
       
      +    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
      +    static int callI() {
      +        return 1;
      +    }
      +
           @Benchmark
      -    public void isNanFloat() {
      +    public void cMoveIsNanFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = Float.isNaN(f1[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void isNanDouble() {
      +    public void cMoveIsNanDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = Double.isNaN(d1[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void isInfiniteFloat() {
      +    public void cMoveIsInfiniteFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = Float.isInfinite(f1[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void isInfiniteDouble() {
      +    public void cMoveIsInfiniteDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = Double.isInfinite(d1[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void isFiniteFloat() {
      +    public void cMoveIsFiniteFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = Float.isFinite(f1[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void isFiniteDouble() {
      +    public void cMoveIsFiniteDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = Double.isFinite(d1[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void equalFloat() {
      +    public void cMoveEqualFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (f1[i] == f2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void equalDouble() {
      +    public void cMoveEqualDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (d1[i] == d2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void lessFloat() {
      +    public void cMoveLessFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (f1[i] < f2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void lessDouble() {
      +    public void cMoveLessDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (d1[i] < d2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void lessEqualFloat() {
      +    public void cMoveLessEqualFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (f1[i] <= f2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void lessEqualDouble() {
      +    public void cMoveLessEqualDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (d1[i] <= d2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void greaterFloat() {
      +    public void cMoveGreaterFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (f1[i] > f2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void greaterDouble() {
      +    public void cMoveGreaterDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (d1[i] > d2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void greaterEqualFloat() {
      +    public void cMoveGreaterEqualFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (f1[i] >= f2[i]) ? 1 : 2;
               }
           }
       
           @Benchmark
      -    public void greaterEqualDouble() {
      +    public void cMoveGreaterEqualDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   res[i] = (d1[i] >= d2[i]) ? 1 : 2;
               }
           }
       
      -    // --------- result: long ---------
      +    @Benchmark
      +    public void branchIsNanFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = Float.isNaN(f1[i]) ? callI() : 2;
      +        }
      +    }
       
           @Benchmark
      -    public void equalFloatResLong() {
      +    public void branchIsNanDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = Double.isNaN(d1[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchIsInfiniteFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = Float.isInfinite(f1[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchIsInfiniteDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = Double.isInfinite(d1[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchIsFiniteFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = Float.isFinite(f1[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchIsFiniteDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = Double.isFinite(d1[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchEqualFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (f1[i] == f2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchEqualDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (d1[i] == d2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (f1[i] < f2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (d1[i] < d2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessEqualFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (f1[i] <= f2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessEqualDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (d1[i] <= d2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (f1[i] > f2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (d1[i] > d2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterEqualFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (f1[i] >= f2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterEqualDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            res[i] = (d1[i] >= d2[i]) ? callI() : 2;
      +        }
      +    }
      +
      +    // --------- result: long ---------
      +
      +    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
      +    static long callL() {
      +        return Long.MAX_VALUE;
      +    }
      +
      +    @Benchmark
      +    public void cMoveEqualFloatResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (f1[i] == f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void equalDoubleResLong() {
      +    public void cMoveEqualDoubleResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (d1[i] == d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void lessFloatResLong() {
      +    public void cMoveLessFloatResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (f1[i] < f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void lessDoubleResLong() {
      +    public void cMoveLessDoubleResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (d1[i] < d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void lessEqualFloatResLong() {
      +    public void cMoveLessEqualFloatResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (f1[i] <= f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void lessEqualDoubleResLong() {
      +    public void cMoveLessEqualDoubleResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (d1[i] <= d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void greaterFloatResLong() {
      +    public void cMoveGreaterFloatResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (f1[i] > f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void greaterDoubleResLong() {
      +    public void cMoveGreaterDoubleResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (d1[i] > d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void greaterEqualFloatResLong() {
      +    public void cMoveGreaterEqualFloatResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (f1[i] >= f2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
           @Benchmark
      -    public void greaterEqualDoubleResLong() {
      +    public void cMoveGreaterEqualDoubleResLong() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resLong[i] = (d1[i] >= d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE;
               }
           }
       
      -    // --------- result: float ---------
      +    @Benchmark
      +    public void branchEqualFloatResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (f1[i] == f2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
       
           @Benchmark
      -    public void equalFloatResFloat() {
      +    public void branchEqualDoubleResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (d1[i] == d2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessFloatResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (f1[i] < f2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessDoubleResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (d1[i] < d2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessEqualFloatResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (f1[i] <= f2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessEqualDoubleResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (d1[i] <= d2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterFloatResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (f1[i] > f2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterDoubleResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (d1[i] > d2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterEqualFloatResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (f1[i] >= f2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterEqualDoubleResLong() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resLong[i] = (d1[i] >= d2[i]) ? callL() : Long.MIN_VALUE;
      +        }
      +    }
      +
      +    // --------- result: float ---------
      +
      +    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
      +    static float callF() {
      +        return 0.1f;
      +    }
      +
      +    @Benchmark
      +    public void cMoveEqualFloatResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (f1[i] == f2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void equalDoubleResFloat() {
      +    public void cMoveEqualDoubleResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (d1[i] == d2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void lessFloatResFloat() {
      +    public void cMoveLessFloatResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (f1[i] < f2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void lessDoubleResFloat() {
      +    public void cMoveLessDoubleResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (d1[i] < d2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void lessEqualFloatResFloat() {
      +    public void cMoveLessEqualFloatResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (f1[i] <= f2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void lessEqualDoubleResFloat() {
      +    public void cMoveLessEqualDoubleResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (d1[i] <= d2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void greaterFloatResFloat() {
      +    public void cMoveGreaterFloatResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (f1[i] > f2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void greaterDoubleResFloat() {
      +    public void cMoveGreaterDoubleResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (d1[i] > d2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void greaterEqualFloatResFloat() {
      +    public void cMoveGreaterEqualFloatResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (f1[i] >= f2[i]) ? 0.1f : 0.2f;
               }
           }
       
           @Benchmark
      -    public void greaterEqualDoubleResFloat() {
      +    public void cMoveGreaterEqualDoubleResFloat() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resFloat[i] = (d1[i] >= d2[i]) ? 0.1f : 0.2f;
               }
           }
       
      -    // --------- result: double ---------
      +    @Benchmark
      +    public void branchEqualFloatResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (f1[i] == f2[i]) ? callF() : 0.2f;
      +        }
      +    }
       
           @Benchmark
      -    public void equalFloatResDouble() {
      +    public void branchEqualDoubleResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (d1[i] == d2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessFloatResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (f1[i] < f2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessDoubleResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (d1[i] < d2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessEqualFloatResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (f1[i] <= f2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessEqualDoubleResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (d1[i] <= d2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterFloatResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (f1[i] > f2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterDoubleResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (d1[i] > d2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterEqualFloatResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (f1[i] >= f2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterEqualDoubleResFloat() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resFloat[i] = (d1[i] >= d2[i]) ? callF() : 0.2f;
      +        }
      +    }
      +
      +    // --------- result: double ---------
      +
      +    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
      +    static double callD() {
      +        return 0.1;
      +    }
      +
      +    @Benchmark
      +    public void cMoveEqualFloatResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (f1[i] == f2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void equalDoubleResDouble() {
      +    public void cMoveEqualDoubleResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (d1[i] == d2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void lessFloatResDouble() {
      +    public void cMoveLessFloatResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (f1[i] < f2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void lessDoubleResDouble() {
      +    public void cMoveLessDoubleResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (d1[i] < d2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void lessEqualFloatResDouble() {
      +    public void cMoveLessEqualFloatResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (f1[i] <= f2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void lessEqualDoubleResDouble() {
      +    public void cMoveLessEqualDoubleResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (d1[i] <= d2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void greaterFloatResDouble() {
      +    public void cMoveGreaterFloatResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (f1[i] > f2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void greaterDoubleResDouble() {
      +    public void cMoveGreaterDoubleResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (d1[i] > d2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void greaterEqualFloatResDouble() {
      +    public void cMoveGreaterEqualFloatResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (f1[i] >= f2[i]) ? 0.1 : 0.2;
               }
           }
       
           @Benchmark
      -    public void greaterEqualDoubleResDouble() {
      +    public void cMoveGreaterEqualDoubleResDouble() {
               for (int i = 0; i < INVOCATIONS; i++) {
                   resDouble[i] = (d1[i] >= d2[i]) ? 0.1 : 0.2;
               }
           }
      +
      +    @Benchmark
      +    public void branchEqualFloatResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (f1[i] == f2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchEqualDoubleResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (d1[i] == d2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessFloatResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (f1[i] < f2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessDoubleResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (d1[i] < d2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessEqualFloatResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (f1[i] <= f2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchLessEqualDoubleResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (d1[i] <= d2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterFloatResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (f1[i] > f2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterDoubleResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (d1[i] > d2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterEqualFloatResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (f1[i] >= f2[i]) ? callD() : 0.2;
      +        }
      +    }
      +
      +    @Benchmark
      +    public void branchGreaterEqualDoubleResDouble() {
      +        for (int i = 0; i < INVOCATIONS; i++) {
      +            resDouble[i] = (d1[i] >= d2[i]) ? callD() : 0.2;
      +        }
      +    }
       }
      
      From 57eb9c79b050224c6bf402ebe7d18afff1f5ce09 Mon Sep 17 00:00:00 2001
      From: Ben Taylor 
      Date: Mon, 9 Feb 2026 20:00:51 +0000
      Subject: [PATCH 183/215] 8377043: Shenandoah: Convert ShenandoahHeapRegion
       related code to use Atomic
      
      Reviewed-by: xpeng, cslucas, kdnilsen, wkemper
      ---
       .../gc/shenandoah/shenandoahHeapRegion.cpp    | 15 +++++----
       .../gc/shenandoah/shenandoahHeapRegion.hpp    | 11 ++++---
       .../shenandoahHeapRegion.inline.hpp           | 13 ++++----
       .../gc/shenandoah/vmStructs_shenandoah.hpp    | 31 ++++++++++---------
       4 files changed, 35 insertions(+), 35 deletions(-)
      
      diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
      index 6bb8382de0a..b0c13df6c4f 100644
      --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
      +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
      @@ -43,7 +43,6 @@
       #include "memory/resourceArea.hpp"
       #include "memory/universe.hpp"
       #include "oops/oop.inline.hpp"
      -#include "runtime/atomicAccess.hpp"
       #include "runtime/globals_extension.hpp"
       #include "runtime/java.hpp"
       #include "runtime/mutexLocker.hpp"
      @@ -384,7 +383,7 @@ size_t ShenandoahHeapRegion::get_plab_allocs() const {
       
       void ShenandoahHeapRegion::set_live_data(size_t s) {
         assert(Thread::current()->is_VM_thread(), "by VM thread");
      -  _live_data = (s >> LogHeapWordSize);
      +  _live_data.store_relaxed(s >> LogHeapWordSize);
       }
       
       void ShenandoahHeapRegion::print_on(outputStream* st) const {
      @@ -435,7 +434,7 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const {
         st->print("|TAMS " SHR_PTR_FORMAT,
                   p2i(ShenandoahHeap::heap()->marking_context()->top_at_mark_start(const_cast(this))));
         st->print("|UWM " SHR_PTR_FORMAT,
      -            p2i(_update_watermark));
      +            p2i(_update_watermark.load_relaxed()));
         st->print("|U %5zu%1s", byte_size_in_proper_unit(used()),                proper_unit_for_byte_size(used()));
         st->print("|T %5zu%1s", byte_size_in_proper_unit(get_tlab_allocs()),     proper_unit_for_byte_size(get_tlab_allocs()));
         st->print("|G %5zu%1s", byte_size_in_proper_unit(get_gclab_allocs()),    proper_unit_for_byte_size(get_gclab_allocs()));
      @@ -839,20 +838,20 @@ void ShenandoahHeapRegion::set_state(RegionState to) {
           evt.set_to(to);
           evt.commit();
         }
      -  AtomicAccess::store(&_state, to);
      +  _state.store_relaxed(to);
       }
       
       void ShenandoahHeapRegion::record_pin() {
      -  AtomicAccess::add(&_critical_pins, (size_t)1);
      +  _critical_pins.add_then_fetch((size_t)1);
       }
       
       void ShenandoahHeapRegion::record_unpin() {
         assert(pin_count() > 0, "Region %zu should have non-zero pins", index());
      -  AtomicAccess::sub(&_critical_pins, (size_t)1);
      +  _critical_pins.sub_then_fetch((size_t)1);
       }
       
       size_t ShenandoahHeapRegion::pin_count() const {
      -  return AtomicAccess::load(&_critical_pins);
      +  return _critical_pins.load_relaxed();
       }
       
       void ShenandoahHeapRegion::set_affiliation(ShenandoahAffiliation new_affiliation) {
      @@ -864,7 +863,7 @@ void ShenandoahHeapRegion::set_affiliation(ShenandoahAffiliation new_affiliation
           log_debug(gc)("Setting affiliation of Region %zu from %s to %s, top: " PTR_FORMAT ", TAMS: " PTR_FORMAT
                         ", watermark: " PTR_FORMAT ", top_bitmap: " PTR_FORMAT,
                         index(), shenandoah_affiliation_name(region_affiliation), shenandoah_affiliation_name(new_affiliation),
      -                  p2i(top()), p2i(ctx->top_at_mark_start(this)), p2i(_update_watermark), p2i(ctx->top_bitmap(this)));
      +                  p2i(top()), p2i(ctx->top_at_mark_start(this)), p2i(_update_watermark.load_relaxed()), p2i(ctx->top_bitmap(this)));
         }
       
       #ifdef ASSERT
      diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
      index 9da2816e2c9..3a0ac042f57 100644
      --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
      +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp
      @@ -34,6 +34,7 @@
       #include "gc/shenandoah/shenandoahAsserts.hpp"
       #include "gc/shenandoah/shenandoahHeap.hpp"
       #include "gc/shenandoah/shenandoahPadding.hpp"
      +#include "runtime/atomic.hpp"
       #include "utilities/sizes.hpp"
       
       class VMStructs;
      @@ -217,7 +218,7 @@ public:
         bool is_alloc_allowed()          const { auto cur_state = state(); return is_empty_state(cur_state) || cur_state == _regular || cur_state == _pinned; }
         bool is_stw_move_allowed()       const { auto cur_state = state(); return cur_state == _regular || cur_state == _cset || (ShenandoahHumongousMoves && cur_state == _humongous_start); }
       
      -  RegionState state()              const { return AtomicAccess::load(&_state); }
      +  RegionState state()              const { return _state.load_relaxed(); }
         int  state_ordinal()             const { return region_state_to_ordinal(state()); }
       
         void record_pin();
      @@ -247,7 +248,7 @@ private:
         HeapWord* _top_before_promoted;
       
         // Seldom updated fields
      -  volatile RegionState _state;
      +  Atomic _state;
         HeapWord* _coalesce_and_fill_boundary; // for old regions not selected as collection set candidates.
       
         // Frequently updated fields
      @@ -257,12 +258,12 @@ private:
         size_t _gclab_allocs;
         size_t _plab_allocs;
       
      -  volatile size_t _live_data;
      -  volatile size_t _critical_pins;
      +  Atomic _live_data;
      +  Atomic _critical_pins;
       
         size_t _mixed_candidate_garbage_words;
       
      -  HeapWord* volatile _update_watermark;
      +  Atomic _update_watermark;
       
         uint _age;
         bool _promoted_in_place;
      diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp
      index be982433885..39b7c732703 100644
      --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp
      +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp
      @@ -32,7 +32,6 @@
       #include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
       #include "gc/shenandoah/shenandoahHeap.inline.hpp"
       #include "gc/shenandoah/shenandoahOldGeneration.hpp"
      -#include "runtime/atomicAccess.hpp"
       
       HeapWord* ShenandoahHeapRegion::allocate_aligned(size_t size, ShenandoahAllocRequest &req, size_t alignment_in_bytes) {
         shenandoah_assert_heaplocked_or_safepoint();
      @@ -147,16 +146,16 @@ inline void ShenandoahHeapRegion::increase_live_data_gc_words(size_t s) {
       }
       
       inline void ShenandoahHeapRegion::internal_increase_live_data(size_t s) {
      -  AtomicAccess::add(&_live_data, s, memory_order_relaxed);
      +  _live_data.add_then_fetch(s, memory_order_relaxed);
       }
       
       inline void ShenandoahHeapRegion::clear_live_data() {
      -  AtomicAccess::store(&_live_data, (size_t)0);
      +  _live_data.store_relaxed((size_t)0);
         _promoted_in_place = false;
       }
       
       inline size_t ShenandoahHeapRegion::get_live_data_words() const {
      -  return AtomicAccess::load(&_live_data);
      +  return _live_data.load_relaxed();
       }
       
       inline size_t ShenandoahHeapRegion::get_live_data_bytes() const {
      @@ -205,21 +204,21 @@ inline size_t ShenandoahHeapRegion::garbage_before_padded_for_promote() const {
       }
       
       inline HeapWord* ShenandoahHeapRegion::get_update_watermark() const {
      -  HeapWord* watermark = AtomicAccess::load_acquire(&_update_watermark);
      +  HeapWord* watermark = _update_watermark.load_acquire();
         assert(bottom() <= watermark && watermark <= top(), "within bounds");
         return watermark;
       }
       
       inline void ShenandoahHeapRegion::set_update_watermark(HeapWord* w) {
         assert(bottom() <= w && w <= top(), "within bounds");
      -  AtomicAccess::release_store(&_update_watermark, w);
      +  _update_watermark.release_store(w);
       }
       
       // Fast version that avoids synchronization, only to be used at safepoints.
       inline void ShenandoahHeapRegion::set_update_watermark_at_safepoint(HeapWord* w) {
         assert(bottom() <= w && w <= top(), "within bounds");
         assert(SafepointSynchronize::is_at_safepoint(), "Should be at Shenandoah safepoint");
      -  _update_watermark = w;
      +  _update_watermark.store_relaxed(w);
       }
       
       inline ShenandoahAffiliation ShenandoahHeapRegion::affiliation() const {
      diff --git a/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp b/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp
      index 3968575d089..e5e2b14a3a1 100644
      --- a/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp
      +++ b/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp
      @@ -29,21 +29,22 @@
       #include "gc/shenandoah/shenandoahHeap.hpp"
       #include "gc/shenandoah/shenandoahHeapRegion.hpp"
       #include "gc/shenandoah/shenandoahMonitoringSupport.hpp"
      +#include "runtime/atomic.hpp"
       
      -#define VM_STRUCTS_SHENANDOAH(nonstatic_field, volatile_nonstatic_field, static_field)                \
      -  nonstatic_field(ShenandoahHeap, _num_regions,                    size_t)                            \
      -  nonstatic_field(ShenandoahHeap, _regions,                        ShenandoahHeapRegion**)            \
      -  nonstatic_field(ShenandoahHeap, _log_min_obj_alignment_in_bytes, int)                               \
      -  nonstatic_field(ShenandoahHeap, _free_set,                       ShenandoahFreeSet*)                \
      -  volatile_nonstatic_field(ShenandoahHeap, _committed,             size_t)                            \
      -  static_field(ShenandoahHeapRegion, RegionSizeBytes,              size_t)                            \
      -  static_field(ShenandoahHeapRegion, RegionSizeBytesShift,         size_t)                            \
      -  volatile_nonstatic_field(ShenandoahHeapRegion, _state,           ShenandoahHeapRegion::RegionState) \
      -  nonstatic_field(ShenandoahHeapRegion, _index,                    size_t const)                      \
      -  nonstatic_field(ShenandoahHeapRegion, _bottom,                   HeapWord* const)                   \
      -  nonstatic_field(ShenandoahHeapRegion, _top,                      HeapWord*)                         \
      -  nonstatic_field(ShenandoahHeapRegion, _end,                      HeapWord* const)                   \
      -  nonstatic_field(ShenandoahFreeSet, _total_global_used,           size_t)                            \
      +#define VM_STRUCTS_SHENANDOAH(nonstatic_field, volatile_nonstatic_field, static_field)                        \
      +  nonstatic_field(ShenandoahHeap, _num_regions,                    size_t)                                    \
      +  nonstatic_field(ShenandoahHeap, _regions,                        ShenandoahHeapRegion**)                    \
      +  nonstatic_field(ShenandoahHeap, _log_min_obj_alignment_in_bytes, int)                                       \
      +  nonstatic_field(ShenandoahHeap, _free_set,                       ShenandoahFreeSet*)                        \
      +  volatile_nonstatic_field(ShenandoahHeap, _committed,             size_t)                                    \
      +  static_field(ShenandoahHeapRegion, RegionSizeBytes,              size_t)                                    \
      +  static_field(ShenandoahHeapRegion, RegionSizeBytesShift,         size_t)                                    \
      +  nonstatic_field(ShenandoahHeapRegion, _state,                    Atomic) \
      +  nonstatic_field(ShenandoahHeapRegion, _index,                    size_t const)                              \
      +  nonstatic_field(ShenandoahHeapRegion, _bottom,                   HeapWord* const)                           \
      +  nonstatic_field(ShenandoahHeapRegion, _top,                      HeapWord*)                                 \
      +  nonstatic_field(ShenandoahHeapRegion, _end,                      HeapWord* const)                           \
      +  nonstatic_field(ShenandoahFreeSet, _total_global_used,           size_t)                                    \
       
       #define VM_INT_CONSTANTS_SHENANDOAH(declare_constant, declare_constant_with_value) \
         declare_constant(ShenandoahHeapRegion::_empty_uncommitted)                       \
      @@ -65,7 +66,7 @@
         declare_toplevel_type(ShenandoahHeapRegion)                                 \
         declare_toplevel_type(ShenandoahHeap*)                                      \
         declare_toplevel_type(ShenandoahHeapRegion*)                                \
      -  declare_toplevel_type(ShenandoahHeapRegion::RegionState)                    \
      +  declare_toplevel_type(Atomic)            \
         declare_toplevel_type(ShenandoahFreeSet)                                    \
         declare_toplevel_type(ShenandoahFreeSet*)                                   \
       
      
      From f9ded7f88cce75151cec32d1ef1f9662ea10431a Mon Sep 17 00:00:00 2001
      From: Sergey Bylokhov 
      Date: Mon, 9 Feb 2026 21:07:51 +0000
      Subject: [PATCH 184/215] 6441373: Editing JTable is not Serializable
      
      Reviewed-by: psadhukhan
      ---
       .../share/classes/javax/swing/JTable.java     |   5 +
       .../swing/JTable/JTableSerialization.java     | 169 ++++++++++++++++++
       2 files changed, 174 insertions(+)
       create mode 100644 test/jdk/javax/swing/JTable/JTableSerialization.java
      
      diff --git a/src/java.desktop/share/classes/javax/swing/JTable.java b/src/java.desktop/share/classes/javax/swing/JTable.java
      index f5c914135d1..fa8110d1517 100644
      --- a/src/java.desktop/share/classes/javax/swing/JTable.java
      +++ b/src/java.desktop/share/classes/javax/swing/JTable.java
      @@ -6021,6 +6021,8 @@ public class JTable extends JComponent implements TableModelListener, Scrollable
       
               surrendersFocusOnKeystroke = f.get("surrendersFocusOnKeystroke", false);
               editorRemover = (PropertyChangeListener) f.get("editorRemover", null);
      +        editingColumn = -1;
      +        editingRow = -1;
               columnSelectionAdjusting = f.get("columnSelectionAdjusting", false);
               rowSelectionAdjusting = f.get("rowSelectionAdjusting", false);
               printError = (Throwable) f.get("printError", null);
      @@ -6053,6 +6055,9 @@ public class JTable extends JComponent implements TableModelListener, Scrollable
            * do any Swing-specific pre-serialization configuration.
            */
           void compWriteObjectNotify() {
      +        if (isEditing() && !getCellEditor().stopCellEditing()) {
      +            getCellEditor().cancelCellEditing();
      +        }
               super.compWriteObjectNotify();
               // If ToolTipText != null, then the tooltip has already been
               // unregistered by JComponent.compWriteObjectNotify()
      diff --git a/test/jdk/javax/swing/JTable/JTableSerialization.java b/test/jdk/javax/swing/JTable/JTableSerialization.java
      new file mode 100644
      index 00000000000..29d8837bb26
      --- /dev/null
      +++ b/test/jdk/javax/swing/JTable/JTableSerialization.java
      @@ -0,0 +1,169 @@
      +/*
      + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      + * or visit www.oracle.com if you need additional information or have any
      + * questions.
      + */
      +
      +import java.awt.Component;
      +import java.awt.EventQueue;
      +import java.io.ByteArrayInputStream;
      +import java.io.ByteArrayOutputStream;
      +import java.io.IOException;
      +import java.io.ObjectInputStream;
      +import java.io.ObjectOutputStream;
      +import java.util.concurrent.TimeUnit;
      +import java.util.concurrent.atomic.AtomicBoolean;
      +
      +import javax.swing.JLabel;
      +import javax.swing.JTable;
      +import javax.swing.UIManager;
      +import javax.swing.UnsupportedLookAndFeelException;
      +
      +import static javax.swing.UIManager.getInstalledLookAndFeels;
      +
      +/**
      + * @test
      + * @bug 6441373
      + * @summary Checks that editing/non-editing JTable is serializable
      + * @run main/timeout=260/othervm -Xmx32m JTableSerialization
      + */
      +public final class JTableSerialization {
      +
      +    private static JTable table;
      +    private static final int ROW = 1;
      +    private static final int COLUMN = 1;
      +    private static final String SOME_TEST_LABEL = "Some TEST label";
      +    private static final String TEST_EDIT_VALUE = "Test EDIT value";
      +
      +    public static void main(String[] argv) throws Exception {
      +        for (UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) {
      +            AtomicBoolean go = new AtomicBoolean(false);
      +            EventQueue.invokeAndWait(() -> go.set(tryLookAndFeel(laf)));
      +            if (!go.get()) {
      +                continue;
      +            }
      +            for (boolean editing : new boolean[]{true, false}) {
      +                EventQueue.invokeAndWait(JTableSerialization::init);
      +                long endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(20);
      +                while (System.nanoTime() < endtime) {
      +                    // need to jump to/from EDT to flush all pending events
      +                    EventQueue.invokeAndWait(() -> test(editing));
      +                }
      +                EventQueue.invokeAndWait(JTableSerialization::validate);
      +            }
      +        }
      +    }
      +
      +    private static void init() {
      +        JLabel label = new JLabel(SOME_TEST_LABEL);
      +        table = new JTable(2, 2);
      +        table.add(label);
      +        table.setValueAt(TEST_EDIT_VALUE, ROW, COLUMN);
      +        checkNonEditingState(table);
      +    }
      +
      +    private static void test(boolean editing) {
      +        if (editing) {
      +            table.editCellAt(ROW, COLUMN);
      +            checkEditingState(table);
      +        }
      +        table = copyTable(table);
      +        checkNonEditingState(table);
      +    }
      +
      +    private static void validate() {
      +        Object value = table.getValueAt(ROW, COLUMN);
      +        if (!value.equals(TEST_EDIT_VALUE)) {
      +            throw new RuntimeException("Wrong value: " + value);
      +        }
      +        for (Component component : table.getComponents()) {
      +            if (component instanceof JLabel) {
      +                if (((JLabel) component).getText().equals(SOME_TEST_LABEL)) {
      +                    return;
      +                }
      +            }
      +        }
      +        throw new RuntimeException("JLabel is not found");
      +    }
      +
      +
      +    private static void checkNonEditingState(JTable jt) {
      +        if (jt.isEditing()) {
      +            throw new RuntimeException("Should not be editing");
      +        }
      +        if (jt.getEditorComponent() != null) {
      +            throw new RuntimeException("Editor should be null");
      +        }
      +        int row = jt.getEditingRow();
      +        if (row != -1) {
      +            throw new RuntimeException("Expected row -1 but was: " + row);
      +        }
      +        int column = jt.getEditingColumn();
      +        if (column != -1) {
      +            throw new RuntimeException("Expected column -1 but was: " + column);
      +        }
      +    }
      +
      +    private static void checkEditingState(JTable jt) {
      +        if (!jt.isEditing()) {
      +            throw new RuntimeException("Should be editing");
      +        }
      +        if (jt.getEditorComponent() == null) {
      +            throw new RuntimeException("Editor should not be null");
      +        }
      +        if (jt.getEditingRow() != ROW) {
      +            throw new RuntimeException("Row should be: " + ROW);
      +        }
      +        if (jt.getEditingColumn() != COLUMN) {
      +            throw new RuntimeException("Column should be: " + COLUMN);
      +        }
      +    }
      +
      +    private static JTable copyTable(JTable jt) {
      +        try {
      +            byte[] bdata;
      +            try (var baos = new ByteArrayOutputStream();
      +                 var oos = new ObjectOutputStream(baos))
      +            {
      +                oos.writeObject(jt);
      +                bdata = baos.toByteArray();
      +            }
      +            try (var bais = new ByteArrayInputStream(bdata);
      +                 var ois = new ObjectInputStream(bais))
      +            {
      +                return (JTable) ois.readObject();
      +            }
      +        } catch (IOException | ClassNotFoundException e) {
      +            throw new RuntimeException(e);
      +        }
      +    }
      +
      +    private static boolean tryLookAndFeel(UIManager.LookAndFeelInfo laf) {
      +        try {
      +            UIManager.setLookAndFeel(laf.getClassName());
      +            System.out.println("LookAndFeel: " + laf.getClassName());
      +            return true;
      +        } catch (UnsupportedLookAndFeelException ignored) {
      +            return false;
      +        } catch (Exception e) {
      +            throw new RuntimeException(e);
      +        }
      +    }
      +}
      
      From 87df1bbbe28f2009adda6ca13d0d7e2766c48c88 Mon Sep 17 00:00:00 2001
      From: Alexey Semenyuk 
      Date: Tue, 10 Feb 2026 03:59:44 +0000
      Subject: [PATCH 185/215] 8377513: jpackage: fix Win8365790Test test
      
      Reviewed-by: almatvee
      ---
       .../jpackage/windows/Win8365790Test.java      | 27 +++++++++++++------
       1 file changed, 19 insertions(+), 8 deletions(-)
      
      diff --git a/test/jdk/tools/jpackage/windows/Win8365790Test.java b/test/jdk/tools/jpackage/windows/Win8365790Test.java
      index 6376a16cecc..5a690ea0146 100644
      --- a/test/jdk/tools/jpackage/windows/Win8365790Test.java
      +++ b/test/jdk/tools/jpackage/windows/Win8365790Test.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,6 +27,8 @@ import java.io.IOException;
       import java.nio.file.Files;
       import java.nio.file.Path;
       import java.time.Duration;
      +import java.util.concurrent.CompletableFuture;
      +import java.util.concurrent.Executors;
       import jdk.jpackage.test.AdditionalLauncher;
       import jdk.jpackage.test.Annotations.Test;
       import jdk.jpackage.test.CfgFile;
      @@ -101,13 +103,18 @@ public class Win8365790Test {
       
           private static String runLauncher(JPackageCommand cmd, String launcherName, Path traceFile, Path outputFile) throws IOException {
               // Launch the specified launcher and send Ctrl+C signal to it.
      -        Thread.ofVirtual().start(() -> {
      -            configureAndExecute(0, Executor.of("powershell", "-NonInteractive", "-NoLogo", "-NoProfile", "-ExecutionPolicy", "Unrestricted")
      -                    .addArgument("-File").addArgument(TEST_PS1)
      -                    .addArguments("-TimeoutSeconds", Long.toString(Duration.ofSeconds(5).getSeconds()))
      -                    .addArgument("-Executable").addArgument(cmd.appLauncherPath(launcherName))
      -                    .dumpOutput());
      -        });
      +
      +        var state = TKit.state();
      +
      +        var future = CompletableFuture.runAsync(() -> {
      +            TKit.withState(() -> {
      +                configureAndExecute(0, Executor.of("powershell", "-NonInteractive", "-NoLogo", "-NoProfile", "-ExecutionPolicy", "Unrestricted")
      +                        .addArgument("-File").addArgument(TEST_PS1)
      +                        .addArguments("-TimeoutSeconds", Long.toString(Duration.ofSeconds(5).getSeconds()))
      +                        .addArgument("-Executable").addArgument(cmd.appLauncherPath(launcherName))
      +                        .dumpOutput());
      +            }, state);
      +        }, Executors.newVirtualThreadPerTaskExecutor());
       
               TKit.waitForFileCreated(traceFile, Duration.ofSeconds(20), Duration.ofSeconds(2));
       
      @@ -118,6 +125,10 @@ public class Win8365790Test {
               }
       
               TKit.assertFileExists(outputFile);
      +
      +        // Call join() on the future to make the test fail if the future execution resulted in a throw.
      +        future.join();
      +
               return Files.readString(outputFile);
           }
       
      
      From 996ca4b44bff2f782b775ee7ca496544e5982774 Mon Sep 17 00:00:00 2001
      From: Alan Bateman 
      Date: Tue, 10 Feb 2026 06:16:10 +0000
      Subject: [PATCH 186/215] 8377411:
       java/lang/Thread/virtual/stress/ParkAfterTimedPark.java only testing pinned
       case
      
      Reviewed-by: vklang
      ---
       .../jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/test/jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java b/test/jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java
      index 1b173271a79..7dd0ac6e5a2 100644
      --- a/test/jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java
      +++ b/test/jdk/java/lang/Thread/virtual/stress/ParkAfterTimedPark.java
      @@ -53,7 +53,7 @@ public class ParkAfterTimedPark {
               for (int i = 1; i <= iterations; i++) {
                   System.out.println(Instant.now() + " => " + i + " of " + iterations);
                   for (int timeout = 1; timeout <= 10; timeout++) {
      -                test(timeout, true);
      +                test(timeout, pinned);
                   }
               }
           }
      
      From b8088941c8f1ef803bd0592b945d3e1ab5c15bee Mon Sep 17 00:00:00 2001
      From: Varada M 
      Date: Tue, 10 Feb 2026 07:28:04 +0000
      Subject: [PATCH 187/215] 8377355: VectorAPI source generation broken after
       JDK-8371187
      
      Reviewed-by: liach, jbhateja
      ---
       .../classes/jdk/incubator/vector/ByteVector.java |  1 -
       .../jdk/incubator/vector/X-Vector.java.template  | 16 ++++++++++++++++
       2 files changed, 16 insertions(+), 1 deletion(-)
      
      diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java
      index 64d8e3a8252..36609807774 100644
      --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java
      +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/ByteVector.java
      @@ -4105,7 +4105,6 @@ public abstract class ByteVector extends AbstractVector {
               return this;
           }
       
      -    /*package-private*/
           @Override
           @ForceInline
           final
      diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template
      index 03883cf3e8a..48d6ed762be 100644
      --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template
      +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template
      @@ -5393,6 +5393,22 @@ public abstract class $abstractvectortype$ extends AbstractVector<$Boxtype$> {
               return this;
           }
       
      +    @Override
      +    @ForceInline
      +    final
      +    $abstractvectortype$ swapIfNeeded(AbstractSpecies srcSpecies) {
      +#if[byte]
      +        return this;
      +#else[byte]
      +        int subLanesPerSrc = subLanesToSwap(srcSpecies);
      +        if (subLanesPerSrc < 0) {
      +            return this;
      +        }
      +        VectorShuffle<$Boxtype$> shuffle = normalizeSubLanesForSpecies(this.vspecies(), subLanesPerSrc);
      +        return ($abstractvectortype$) this.rearrange(shuffle);
      +#end[byte]
      +    }
      +
           static final int ARRAY_SHIFT =
               31 - Integer.numberOfLeadingZeros(Unsafe.ARRAY_$TYPE$_INDEX_SCALE);
           static final long ARRAY_BASE =
      
      From 2c9c2f514be0928d15a0642058b98d73c494572f Mon Sep 17 00:00:00 2001
      From: Thomas Schatzl 
      Date: Tue, 10 Feb 2026 08:27:39 +0000
      Subject: [PATCH 188/215] 8376353: Parallel: Convert PSParallelCompact classes
       to use Atomic
      
      Reviewed-by: iwalulya, ayang
      ---
       .../share/gc/parallel/psParallelCompact.cpp   | 34 ++++++-----
       .../share/gc/parallel/psParallelCompact.hpp   | 60 +++++++++----------
       2 files changed, 50 insertions(+), 44 deletions(-)
      
      diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
      index bab72296d4c..4c6ea01e45f 100644
      --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp
      +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
      @@ -30,6 +30,7 @@
       #include "code/codeCache.hpp"
       #include "code/nmethod.hpp"
       #include "compiler/oopMap.hpp"
      +#include "cppstdlib/new.hpp"
       #include "gc/parallel/objectStartArray.inline.hpp"
       #include "gc/parallel/parallelArguments.hpp"
       #include "gc/parallel/parallelScavengeHeap.inline.hpp"
      @@ -135,8 +136,8 @@ bool ParallelCompactData::RegionData::is_clear() {
                (_source_region == 0) &&
                (_partial_obj_addr == nullptr) &&
                (_partial_obj_size == 0) &&
      -         (_dc_and_los == 0) &&
      -         (_shadow_state == 0);
      +         (dc_and_los() == 0) &&
      +         (shadow_state() == 0);
       }
       
       #ifdef ASSERT
      @@ -145,8 +146,8 @@ void ParallelCompactData::RegionData::verify_clear() {
         assert(_source_region == 0, "inv");
         assert(_partial_obj_addr == nullptr, "inv");
         assert(_partial_obj_size == 0, "inv");
      -  assert(_dc_and_los == 0, "inv");
      -  assert(_shadow_state == 0, "inv");
      +  assert(dc_and_los() == 0, "inv");
      +  assert(shadow_state() == 0, "inv");
       }
       #endif
       
      @@ -296,7 +297,9 @@ void ParallelCompactData::clear_range(size_t beg_region, size_t end_region) {
         assert(end_region <= _region_count, "end_region out of range");
       
         const size_t region_cnt = end_region - beg_region;
      -  memset(_region_data + beg_region, 0, region_cnt * sizeof(RegionData));
      +  for (size_t i = beg_region; i < end_region; i++) {
      +    ::new (&_region_data[i]) RegionData{};
      +  }
       }
       
       // The total live words on src_region would overflow the target space, so find
      @@ -1294,7 +1297,7 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) {
       }
       
       template
      -void PSParallelCompact::adjust_in_space_helper(SpaceId id, volatile uint* claim_counter, Func&& on_stripe) {
      +void PSParallelCompact::adjust_in_space_helper(SpaceId id, Atomic* claim_counter, Func&& on_stripe) {
         MutableSpace* sp = PSParallelCompact::space(id);
         HeapWord* const bottom = sp->bottom();
         HeapWord* const top = sp->top();
      @@ -1307,7 +1310,7 @@ void PSParallelCompact::adjust_in_space_helper(SpaceId id, volatile uint* claim_
         const size_t stripe_size = num_regions_per_stripe * region_size;
       
         while (true) {
      -    uint counter = AtomicAccess::fetch_then_add(claim_counter, num_regions_per_stripe);
      +    uint counter = claim_counter->fetch_then_add(num_regions_per_stripe);
           HeapWord* cur_stripe = bottom + counter * region_size;
           if (cur_stripe >= top) {
             break;
      @@ -1317,7 +1320,7 @@ void PSParallelCompact::adjust_in_space_helper(SpaceId id, volatile uint* claim_
         }
       }
       
      -void PSParallelCompact::adjust_in_old_space(volatile uint* claim_counter) {
      +void PSParallelCompact::adjust_in_old_space(Atomic* claim_counter) {
         // Regions in old-space shouldn't be split.
         assert(!_space_info[old_space_id].split_info().is_valid(), "inv");
       
      @@ -1348,7 +1351,7 @@ void PSParallelCompact::adjust_in_old_space(volatile uint* claim_counter) {
         });
       }
       
      -void PSParallelCompact::adjust_in_young_space(SpaceId id, volatile uint* claim_counter) {
      +void PSParallelCompact::adjust_in_young_space(SpaceId id, Atomic* claim_counter) {
         adjust_in_space_helper(id, claim_counter, [](HeapWord* stripe_start, HeapWord* stripe_end) {
           HeapWord* obj_start = stripe_start;
           while (obj_start < stripe_end) {
      @@ -1362,7 +1365,7 @@ void PSParallelCompact::adjust_in_young_space(SpaceId id, volatile uint* claim_c
         });
       }
       
      -void PSParallelCompact::adjust_pointers_in_spaces(uint worker_id, volatile uint* claim_counters) {
      +void PSParallelCompact::adjust_pointers_in_spaces(uint worker_id, Atomic* claim_counters) {
         auto start_time = Ticks::now();
         adjust_in_old_space(&claim_counters[0]);
         for (uint id = eden_space_id; id < last_space_id; ++id) {
      @@ -1376,12 +1379,12 @@ class PSAdjustTask final : public WorkerTask {
         WeakProcessor::Task                        _weak_proc_task;
         OopStorageSetStrongParState  _oop_storage_iter;
         uint                                       _nworkers;
      -  volatile bool                              _code_cache_claimed;
      -  volatile uint _claim_counters[PSParallelCompact::last_space_id] = {};
      +  Atomic                               _code_cache_claimed;
      +  Atomic _claim_counters[PSParallelCompact::last_space_id];
       
         bool try_claim_code_cache_task() {
      -    return AtomicAccess::load(&_code_cache_claimed) == false
      -        && AtomicAccess::cmpxchg(&_code_cache_claimed, false, true) == false;
      +    return _code_cache_claimed.load_relaxed() == false
      +        && _code_cache_claimed.compare_set(false, true);
         }
       
       public:
      @@ -1393,6 +1396,9 @@ public:
           _nworkers(nworkers),
           _code_cache_claimed(false) {
       
      +    for (unsigned int i = PSParallelCompact::old_space_id; i < PSParallelCompact::last_space_id; ++i) {
      +      ::new (&_claim_counters[i]) Atomic{};
      +    }
           ClassLoaderDataGraph::verify_claimed_marks_cleared(ClassLoaderData::_claim_stw_fullgc_adjust);
         }
       
      diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.hpp b/src/hotspot/share/gc/parallel/psParallelCompact.hpp
      index 4ac9395d727..f5ab041fa97 100644
      --- a/src/hotspot/share/gc/parallel/psParallelCompact.hpp
      +++ b/src/hotspot/share/gc/parallel/psParallelCompact.hpp
      @@ -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
      @@ -34,7 +34,7 @@
       #include "gc/shared/referenceProcessor.hpp"
       #include "gc/shared/taskTerminator.hpp"
       #include "oops/oop.hpp"
      -#include "runtime/atomicAccess.hpp"
      +#include "runtime/atomic.hpp"
       #include "runtime/orderAccess.hpp"
       
       class ParallelScavengeHeap;
      @@ -236,7 +236,7 @@ public:
           // in this region (words).  This does not include the partial object
           // extending onto the region (if any), or the part of an object that extends
           // onto the next region (if any).
      -    size_t live_obj_size() const { return _dc_and_los & los_mask; }
      +    size_t live_obj_size() const { return dc_and_los() & los_mask; }
       
           // Total live data that lies within the region (words).
           size_t data_size() const { return partial_obj_size() + live_obj_size(); }
      @@ -268,9 +268,9 @@ public:
           // Minor subtlety:  claimed() returns true if the region is marked
           // completed(), which is desirable since a region must be claimed before it
           // can be completed.
      -    bool available() const { return _dc_and_los < dc_one; }
      -    bool claimed()   const { return _dc_and_los >= dc_claimed; }
      -    bool completed() const { return _dc_and_los >= dc_completed; }
      +    bool available() const { return dc_and_los() < dc_one; }
      +    bool claimed()   const { return dc_and_los() >= dc_claimed; }
      +    bool completed() const { return dc_and_los() >= dc_completed; }
       
           // These are not atomic.
           void set_destination(HeapWord* addr)       { _destination = addr; }
      @@ -315,7 +315,7 @@ public:
           // Return to the normal path here
           inline void shadow_to_normal();
       
      -    int shadow_state() { return _shadow_state; }
      +    int shadow_state() { return _shadow_state.load_relaxed(); }
       
           bool is_clear();
       
      @@ -339,9 +339,10 @@ public:
           size_t               _source_region;
           HeapWord*            _partial_obj_addr;
           region_sz_t          _partial_obj_size;
      -    region_sz_t volatile _dc_and_los;
      -    int         volatile _shadow_state;
      +    Atomic  _dc_and_los;
      +    Atomic          _shadow_state;
       
      +    region_sz_t dc_and_los() const { return _dc_and_los.load_relaxed(); }
       #ifdef ASSERT
          public:
           uint                 _pushed;   // 0 until region is pushed onto a stack
      @@ -411,7 +412,7 @@ private:
       inline uint
       ParallelCompactData::RegionData::destination_count_raw() const
       {
      -  return _dc_and_los & dc_mask;
      +  return dc_and_los() & dc_mask;
       }
       
       inline uint
      @@ -425,26 +426,26 @@ ParallelCompactData::RegionData::set_destination_count(uint count)
       {
         assert(count <= (dc_completed >> dc_shift), "count too large");
         const region_sz_t live_sz = (region_sz_t) live_obj_size();
      -  _dc_and_los = (count << dc_shift) | live_sz;
      +  _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 = destination_count_raw() | (region_sz_t)words;
      +  _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");
      -  assert(_dc_and_los >= dc_one, "count would go negative");
      -  AtomicAccess::add(&_dc_and_los, dc_mask);
      +  assert(dc_and_los() < dc_claimed, "already claimed");
      +  assert(dc_and_los() >= dc_one, "count would go negative");
      +  _dc_and_los.add_then_fetch(dc_mask);
       }
       
       inline void ParallelCompactData::RegionData::set_completed()
       {
         assert(claimed(), "must be claimed first");
      -  _dc_and_los = dc_completed | (region_sz_t) live_obj_size();
      +  _dc_and_los.store_relaxed(dc_completed | (region_sz_t) live_obj_size());
       }
       
       // MT-unsafe claiming of a region.  Should only be used during single threaded
      @@ -452,7 +453,7 @@ inline void ParallelCompactData::RegionData::set_completed()
       inline bool ParallelCompactData::RegionData::claim_unsafe()
       {
         if (available()) {
      -    _dc_and_los |= dc_claimed;
      +    _dc_and_los.store_relaxed(dc_and_los() | dc_claimed);
           return true;
         }
         return false;
      @@ -461,36 +462,35 @@ inline bool ParallelCompactData::RegionData::claim_unsafe()
       inline void ParallelCompactData::RegionData::add_live_obj(size_t words)
       {
         assert(words <= (size_t)los_mask - live_obj_size(), "overflow");
      -  AtomicAccess::add(&_dc_and_los, static_cast(words));
      +  _dc_and_los.add_then_fetch(static_cast(words));
       }
       
       inline bool ParallelCompactData::RegionData::claim()
       {
         const region_sz_t los = static_cast(live_obj_size());
      -  const region_sz_t old = AtomicAccess::cmpxchg(&_dc_and_los, los, dc_claimed | los);
      -  return old == los;
      +  return _dc_and_los.compare_set(los, dc_claimed | los);
       }
       
       inline bool ParallelCompactData::RegionData::mark_normal() {
      -  return AtomicAccess::cmpxchg(&_shadow_state, UnusedRegion, NormalRegion) == UnusedRegion;
      +  return _shadow_state.compare_set(UnusedRegion, NormalRegion);
       }
       
       inline bool ParallelCompactData::RegionData::mark_shadow() {
      -  if (_shadow_state != UnusedRegion) return false;
      -  return AtomicAccess::cmpxchg(&_shadow_state, UnusedRegion, ShadowRegion) == UnusedRegion;
      +  if (shadow_state() != UnusedRegion) return false;
      +  return _shadow_state.compare_set(UnusedRegion, ShadowRegion);
       }
       
       inline void ParallelCompactData::RegionData::mark_filled() {
      -  int old = AtomicAccess::cmpxchg(&_shadow_state, ShadowRegion, FilledShadow);
      +  int old = _shadow_state.compare_exchange(ShadowRegion, FilledShadow);
         assert(old == ShadowRegion, "Fail to mark the region as filled");
       }
       
       inline bool ParallelCompactData::RegionData::mark_copied() {
      -  return AtomicAccess::cmpxchg(&_shadow_state, FilledShadow, CopiedShadow) == FilledShadow;
      +  return _shadow_state.compare_set(FilledShadow, CopiedShadow);
       }
       
       void ParallelCompactData::RegionData::shadow_to_normal() {
      -  int old = AtomicAccess::cmpxchg(&_shadow_state, ShadowRegion, NormalRegion);
      +  int old = _shadow_state.compare_exchange(ShadowRegion, NormalRegion);
         assert(old == ShadowRegion, "Fail to mark the region as finish");
       }
       
      @@ -764,13 +764,13 @@ public:
         static bool invoke(bool clear_all_soft_refs, bool should_do_max_compaction);
       
         template
      -  static void adjust_in_space_helper(SpaceId id, volatile uint* claim_counter, Func&& on_stripe);
      +  static void adjust_in_space_helper(SpaceId id, Atomic* claim_counter, Func&& on_stripe);
       
      -  static void adjust_in_old_space(volatile uint* claim_counter);
      +  static void adjust_in_old_space(Atomic* claim_counter);
       
      -  static void adjust_in_young_space(SpaceId id, volatile uint* claim_counter);
      +  static void adjust_in_young_space(SpaceId id, Atomic* claim_counter);
       
      -  static void adjust_pointers_in_spaces(uint worker_id, volatile uint* claim_counter);
      +  static void adjust_pointers_in_spaces(uint worker_id, Atomic* claim_counter);
       
         static void post_initialize();
         // Perform initialization for PSParallelCompact that requires
      
      From f124f86f4304fbb62aabdef8f2d480d197aaa1b3 Mon Sep 17 00:00:00 2001
      From: Thomas Schatzl 
      Date: Tue, 10 Feb 2026 08:30:40 +0000
      Subject: [PATCH 189/215] 8376666: Convert G1BlockOffsetTable to use Atomic
      
      Reviewed-by: iwalulya, ayang
      ---
       .../share/gc/g1/g1BlockOffsetTable.cpp        | 48 +++++++++----------
       .../share/gc/g1/g1BlockOffsetTable.hpp        | 25 +++++-----
       .../share/gc/g1/g1BlockOffsetTable.inline.hpp | 15 +++---
       3 files changed, 44 insertions(+), 44 deletions(-)
      
      diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp
      index 4653f96980d..c695ad977fe 100644
      --- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp
      +++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.cpp
      @@ -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
      @@ -40,26 +40,26 @@ G1BlockOffsetTable::G1BlockOffsetTable(MemRegion heap, G1RegionToSpaceMapper* st
       
         MemRegion bot_reserved = storage->reserved();
       
      -  _offset_base = ((uint8_t*)bot_reserved.start() - (uintptr_t(_reserved.start()) >> CardTable::card_shift()));
      +  _offset_base = ((Atomic*)bot_reserved.start() - (uintptr_t(_reserved.start()) >> CardTable::card_shift()));
       
         log_trace(gc, bot)("G1BlockOffsetTable::G1BlockOffsetTable: ");
         log_trace(gc, bot)("    rs.base(): " PTR_FORMAT "  rs.size(): %zu  rs end(): " PTR_FORMAT,
                            p2i(bot_reserved.start()), bot_reserved.byte_size(), p2i(bot_reserved.end()));
       }
       
      -void G1BlockOffsetTable::set_offset_array(uint8_t* addr, uint8_t offset) {
      +void G1BlockOffsetTable::set_offset_array(Atomic* addr, uint8_t offset) {
         check_address(addr, "Block offset table address out of range");
      -  AtomicAccess::store(addr, offset);
      +  addr->store_relaxed(offset);
       }
       
      -void G1BlockOffsetTable::set_offset_array(uint8_t* addr, HeapWord* high, HeapWord* low) {
      +void G1BlockOffsetTable::set_offset_array(Atomic* addr, HeapWord* high, HeapWord* low) {
         assert(high >= low, "addresses out of order");
         size_t offset = pointer_delta(high, low);
         check_offset(offset, "offset too large");
         set_offset_array(addr, (uint8_t)offset);
       }
       
      -void G1BlockOffsetTable::set_offset_array(uint8_t* left, uint8_t* right, uint8_t offset) {
      +void G1BlockOffsetTable::set_offset_array(Atomic* left, Atomic* right, uint8_t offset) {
         check_address(right, "Right block offset table address out of range");
         assert(left <= right, "indexes out of order");
         size_t num_cards = right - left + 1;
      @@ -67,9 +67,9 @@ void G1BlockOffsetTable::set_offset_array(uint8_t* left, uint8_t* right, uint8_t
       }
       
       #ifdef ASSERT
      -void G1BlockOffsetTable::check_address(uint8_t* addr, const char* msg) const {
      -  uint8_t* start_addr = const_cast(_offset_base + (uintptr_t(_reserved.start()) >> CardTable::card_shift()));
      -  uint8_t* end_addr = const_cast(_offset_base + (uintptr_t(_reserved.end()) >> CardTable::card_shift()));
      +void G1BlockOffsetTable::check_address(Atomic* addr, const char* msg) const {
      +  Atomic* start_addr = const_cast*>(_offset_base + (uintptr_t(_reserved.start()) >> CardTable::card_shift()));
      +  Atomic* end_addr = const_cast*>(_offset_base + (uintptr_t(_reserved.end()) >> CardTable::card_shift()));
         assert(addr >= start_addr && addr <= end_addr,
                "%s - offset address: " PTR_FORMAT ", start address: " PTR_FORMAT ", end address: " PTR_FORMAT,
                msg, (p2i(addr)), (p2i(start_addr)), (p2i(end_addr)));
      @@ -113,17 +113,17 @@ void G1BlockOffsetTable::check_address(uint8_t* addr, const char* msg) const {
       //      Move back N (e.g., 8) entries and repeat with the
       //        value of the new entry
       //
      -void G1BlockOffsetTable::set_remainder_to_point_to_start_incl(uint8_t* start_card, uint8_t* end_card) {
      +void G1BlockOffsetTable::set_remainder_to_point_to_start_incl(Atomic* start_card, Atomic* end_card) {
         assert(start_card <= end_card, "precondition");
         assert(offset_array(start_card-1) < CardTable::card_size_in_words(),
                "Offset card has an unexpected value");
      -  uint8_t* start_card_for_region = start_card;
      +  Atomic* start_card_for_region = start_card;
         uint8_t offset = UINT8_MAX;
         for (uint i = 0; i < BOTConstants::N_powers; i++) {
           // -1 so that the card with the actual offset is counted.  Another -1
           // so that the reach ends in this region and not at the start
           // of the next.
      -    uint8_t* reach = start_card - 1 + (BOTConstants::power_to_cards_back(i+1) - 1);
      +    Atomic* reach = start_card - 1 + (BOTConstants::power_to_cards_back(i+1) - 1);
           offset = CardTable::card_size_in_words() + i;
           if (reach >= end_card) {
             set_offset_array(start_card_for_region, end_card, offset);
      @@ -141,12 +141,12 @@ void G1BlockOffsetTable::set_remainder_to_point_to_start_incl(uint8_t* start_car
       // The card-interval [start_card, end_card] is a closed interval; this
       // is an expensive check -- use with care and only under protection of
       // suitable flag.
      -void G1BlockOffsetTable::check_all_cards(uint8_t* start_card, uint8_t* end_card) const {
      +void G1BlockOffsetTable::check_all_cards(Atomic* start_card, Atomic* end_card) const {
         if (end_card < start_card) {
           return;
         }
         guarantee(offset_array(start_card) == CardTable::card_size_in_words(), "Wrong value in second card");
      -  for (uint8_t* c = start_card + 1; c <= end_card; c++ /* yeah! */) {
      +  for (Atomic* c = start_card + 1; c <= end_card; c++ /* yeah! */) {
           uint8_t entry = offset_array(c);
           if ((unsigned)(c - start_card) > BOTConstants::power_to_cards_back(1)) {
             guarantee(entry > CardTable::card_size_in_words(),
      @@ -157,7 +157,7 @@ void G1BlockOffsetTable::check_all_cards(uint8_t* start_card, uint8_t* end_card)
                       (uint)entry, (uint)offset_array(c), CardTable::card_size_in_words());
           }
           size_t backskip = BOTConstants::entry_to_cards_back(entry);
      -    uint8_t* landing_card = c - backskip;
      +    Atomic* landing_card = c - backskip;
           guarantee(landing_card >= (start_card - 1), "Inv");
           if (landing_card >= start_card) {
             guarantee(offset_array(landing_card) <= entry,
      @@ -188,7 +188,7 @@ void G1BlockOffsetTable::check_all_cards(uint8_t* start_card, uint8_t* end_card)
       //
       void G1BlockOffsetTable::update_for_block_work(HeapWord* blk_start, HeapWord* blk_end) {
         HeapWord* const cur_card_boundary = align_up_by_card_size(blk_start);
      -  uint8_t* const offset_card = entry_for_addr(cur_card_boundary);
      +  Atomic* const offset_card = entry_for_addr(cur_card_boundary);
       
         assert(blk_start != nullptr && blk_end > blk_start,
                "phantom block");
      @@ -209,7 +209,7 @@ void G1BlockOffsetTable::update_for_block_work(HeapWord* blk_start, HeapWord* bl
         // We need to now mark the subsequent cards that this block spans.
       
         // Index of card on which the block ends.
      -  uint8_t* end_card = entry_for_addr(blk_end - 1);
      +  Atomic* end_card = entry_for_addr(blk_end - 1);
       
         // Are there more cards left to be updated?
         if (offset_card + 1 <= end_card) {
      @@ -224,7 +224,7 @@ void G1BlockOffsetTable::update_for_block_work(HeapWord* blk_start, HeapWord* bl
       
         // The offset can be 0 if the block starts on a boundary.  That
         // is checked by an assertion above.
      -  uint8_t* previous_card = entry_for_addr(blk_start);
      +  Atomic* previous_card = entry_for_addr(blk_start);
         HeapWord* boundary = addr_for_entry(previous_card);
         assert((offset_array(offset_card) == 0 && blk_start == boundary) ||
                (offset_array(offset_card) > 0 && offset_array(offset_card) < CardTable::card_size_in_words()),
      @@ -240,7 +240,7 @@ void G1BlockOffsetTable::update_for_block_work(HeapWord* blk_start, HeapWord* bl
       }
       
       #ifdef ASSERT
      -void G1BlockOffsetTable::verify_offset(uint8_t* card_index, uint8_t upper_boundary) const {
      +void G1BlockOffsetTable::verify_offset(Atomic* card_index, uint8_t upper_boundary) const {
         assert(offset_array(card_index) <= upper_boundary,
                "Offset %u should not be larger than upper boundary %u.",
                (uint) offset_array(card_index),
      @@ -250,19 +250,19 @@ void G1BlockOffsetTable::verify_offset(uint8_t* card_index, uint8_t upper_bounda
       void G1BlockOffsetTable::verify_for_block(HeapWord* blk_start, HeapWord* blk_end) const {
         assert(is_crossing_card_boundary(blk_start, blk_end), "precondition");
       
      -  uint8_t* start_card = entry_for_addr(align_up_by_card_size(blk_start));
      -  uint8_t* end_card = entry_for_addr(blk_end - 1);
      +  Atomic* start_card = entry_for_addr(align_up_by_card_size(blk_start));
      +  Atomic* end_card = entry_for_addr(blk_end - 1);
         // Check cards in [start_card, end_card]
         verify_offset(start_card, CardTable::card_size_in_words());
       
      -  for (uint8_t* current_card = start_card + 1; current_card <= end_card; ++current_card) {
      +  for (Atomic* current_card = start_card + 1; current_card <= end_card; ++current_card) {
           assert(offset_array(current_card) > 0,
                  "Offset %u is not larger than 0.",
                  (uint) offset_array(current_card));
           verify_offset(current_card, (uint8_t) (CardTable::card_size_in_words() + BOTConstants::N_powers - 1));
       
      -    uint8_t* prev  = current_card - 1;
      -    uint8_t* value = current_card;
      +    Atomic* prev  = current_card - 1;
      +    Atomic* value = current_card;
           if (offset_array(prev) != offset_array(value)) {
             assert(offset_array(value) >= offset_array(prev), "monotonic");
             size_t n_cards_back = BOTConstants::entry_to_cards_back(offset_array(value));
      diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.hpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.hpp
      index 3b97efc4f0f..89c68ce96d2 100644
      --- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.hpp
      +++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.hpp
      @@ -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
      @@ -29,6 +29,7 @@
       #include "gc/shared/blockOffsetTable.hpp"
       #include "gc/shared/cardTable.hpp"
       #include "memory/memRegion.hpp"
      +#include "runtime/atomic.hpp"
       #include "utilities/globalDefinitions.hpp"
       
       // This implementation of "G1BlockOffsetTable" divides the covered region
      @@ -41,7 +42,7 @@ private:
         MemRegion _reserved;
       
         // Biased array-start of BOT array for fast BOT entry translation
      -  volatile uint8_t* _offset_base;
      +  Atomic* _offset_base;
       
         void check_offset(size_t offset, const char* msg) const {
           assert(offset < CardTable::card_size_in_words(),
      @@ -51,32 +52,32 @@ private:
       
         // Bounds checking accessors:
         // For performance these have to devolve to array accesses in product builds.
      -  inline uint8_t offset_array(uint8_t* addr) const;
      +  inline uint8_t offset_array(Atomic* addr) const;
       
      -  inline void set_offset_array(uint8_t* addr, uint8_t offset);
      +  inline void set_offset_array(Atomic* addr, uint8_t offset);
       
      -  inline void set_offset_array(uint8_t* addr, HeapWord* high, HeapWord* low);
      +  inline void set_offset_array(Atomic* addr, HeapWord* high, HeapWord* low);
       
      -  inline void set_offset_array(uint8_t* left, uint8_t* right, uint8_t offset);
      +  inline void set_offset_array(Atomic* left, Atomic* right, uint8_t offset);
       
         // Mapping from address to object start array entry
      -  inline uint8_t* entry_for_addr(const void* const p) const;
      +  inline Atomic* entry_for_addr(const void* const p) const;
       
         // Mapping from object start array entry to address of first word
      -  inline HeapWord* addr_for_entry(const uint8_t* const p) const;
      +  inline HeapWord* addr_for_entry(const Atomic* const p) const;
       
      -  void check_address(uint8_t* addr, const char* msg) const NOT_DEBUG_RETURN;
      +  void check_address(Atomic* addr, const char* msg) const NOT_DEBUG_RETURN;
       
         // Sets the entries corresponding to the cards starting at "start" and ending
         // at "end" to point back to the card before "start"; [start, end]
      -  void set_remainder_to_point_to_start_incl(uint8_t* start, uint8_t* end);
      +  void set_remainder_to_point_to_start_incl(Atomic* start, Atomic* end);
       
         // Update BOT entries corresponding to the mem range [blk_start, blk_end).
         void update_for_block_work(HeapWord* blk_start, HeapWord* blk_end);
       
      -  void check_all_cards(uint8_t* left_card, uint8_t* right_card) const NOT_DEBUG_RETURN;
      +  void check_all_cards(Atomic* left_card, Atomic* right_card) const NOT_DEBUG_RETURN;
       
      -  void verify_offset(uint8_t* card_index, uint8_t upper) const NOT_DEBUG_RETURN;
      +  void verify_offset(Atomic* card_index, uint8_t upper) const NOT_DEBUG_RETURN;
         void verify_for_block(HeapWord* blk_start, HeapWord* blk_end) const NOT_DEBUG_RETURN;
       
         static HeapWord* align_up_by_card_size(HeapWord* const addr) {
      diff --git a/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp b/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp
      index 900e9516c1a..0d809b65526 100644
      --- a/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp
      +++ b/src/hotspot/share/gc/g1/g1BlockOffsetTable.inline.hpp
      @@ -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
      @@ -31,12 +31,11 @@
       #include "gc/shared/cardTable.hpp"
       #include "gc/shared/memset_with_concurrent_readers.hpp"
       #include "oops/oop.inline.hpp"
      -#include "runtime/atomicAccess.hpp"
       
       inline HeapWord* G1BlockOffsetTable::block_start_reaching_into_card(const void* addr) const {
         assert(_reserved.contains(addr), "invalid address");
       
      -  uint8_t* entry = entry_for_addr(addr);
      +  Atomic* entry = entry_for_addr(addr);
         uint8_t offset = offset_array(entry);
         while (offset >= CardTable::card_size_in_words()) {
           // The excess of the offset from N_words indicates a power of Base
      @@ -50,19 +49,19 @@ inline HeapWord* G1BlockOffsetTable::block_start_reaching_into_card(const void*
         return q - offset;
       }
       
      -uint8_t G1BlockOffsetTable::offset_array(uint8_t* addr) const {
      +uint8_t G1BlockOffsetTable::offset_array(Atomic* addr) const {
         check_address(addr, "Block offset table address out of range");
      -  return AtomicAccess::load(addr);
      +  return addr->load_relaxed();
       }
       
      -inline uint8_t* G1BlockOffsetTable::entry_for_addr(const void* const p) const {
      +inline Atomic* G1BlockOffsetTable::entry_for_addr(const void* const p) const {
         assert(_reserved.contains(p),
                "out of bounds access to block offset table");
      -  uint8_t* result = const_cast(&_offset_base[uintptr_t(p) >> CardTable::card_shift()]);
      +  Atomic* result = const_cast*>(&_offset_base[uintptr_t(p) >> CardTable::card_shift()]);
         return result;
       }
       
      -inline HeapWord* G1BlockOffsetTable::addr_for_entry(const uint8_t* const p) const {
      +inline HeapWord* G1BlockOffsetTable::addr_for_entry(const Atomic* const p) const {
         // _offset_base can be "negative", so can't use pointer_delta().
         size_t delta = p - _offset_base;
         HeapWord* result = (HeapWord*) (delta << CardTable::card_shift());
      
      From fef06c04e74f509905e2229b0e2d1682aa5d3852 Mon Sep 17 00:00:00 2001
      From: Thomas Schatzl 
      Date: Tue, 10 Feb 2026 08:31:13 +0000
      Subject: [PATCH 190/215] 8376328: Convert PLABStats to use Atomic
      
      Reviewed-by: iwalulya, ayang
      ---
       src/hotspot/share/gc/g1/g1EvacStats.cpp     | 12 ++++-----
       src/hotspot/share/gc/shared/plab.hpp        | 27 +++++++++++----------
       src/hotspot/share/gc/shared/plab.inline.hpp | 11 ++++-----
       3 files changed, 25 insertions(+), 25 deletions(-)
      
      diff --git a/src/hotspot/share/gc/g1/g1EvacStats.cpp b/src/hotspot/share/gc/g1/g1EvacStats.cpp
      index 1d54b184e64..d93f63383c4 100644
      --- a/src/hotspot/share/gc/g1/g1EvacStats.cpp
      +++ b/src/hotspot/share/gc/g1/g1EvacStats.cpp
      @@ -48,11 +48,11 @@ void G1EvacStats::log_plab_allocation() {
                             "used: %zuB, "
                             "undo waste: %zuB, ",
                             _description,
      -                      _allocated * HeapWordSize,
      -                      _wasted * HeapWordSize,
      -                      _unused * HeapWordSize,
      +                      allocated() * HeapWordSize,
      +                      wasted() * HeapWordSize,
      +                      unused() * HeapWordSize,
                             used() * HeapWordSize,
      -                      _undo_wasted * HeapWordSize);
      +                      undo_wasted() * HeapWordSize);
         log_debug(gc, plab)("%s other allocation: "
                             "region end waste: %zuB, "
                             "regions filled: %u, "
      @@ -157,13 +157,13 @@ void G1EvacStats::adjust_desired_plab_size() {
           assert(is_object_aligned(max_size()) && min_size() <= max_size(),
                  "PLAB clipping computation may be incorrect");
       
      -    assert(_allocated != 0 || _unused == 0,
      +    assert(allocated() != 0 || unused() == 0,
                  "Inconsistency in PLAB stats: "
                  "_allocated: %zu, "
                  "_wasted: %zu, "
                  "_unused: %zu, "
                  "_undo_wasted: %zu",
      -           _allocated, _wasted, _unused, _undo_wasted);
      +           allocated(), wasted(), unused(), undo_wasted());
       
           size_t plab_size = compute_desired_plab_size();
           // Take historical weighted average
      diff --git a/src/hotspot/share/gc/shared/plab.hpp b/src/hotspot/share/gc/shared/plab.hpp
      index 2eebdeeadb4..5200f022633 100644
      --- a/src/hotspot/share/gc/shared/plab.hpp
      +++ b/src/hotspot/share/gc/shared/plab.hpp
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2001, 2023, 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
      @@ -27,6 +27,7 @@
       
       #include "gc/shared/collectedHeap.hpp"
       #include "memory/allocation.hpp"
      +#include "runtime/atomic.hpp"
       #include "utilities/globalDefinitions.hpp"
       
       // Forward declarations.
      @@ -149,16 +150,16 @@ class PLABStats : public CHeapObj {
       protected:
         const char* _description;   // Identifying string.
       
      -  size_t _allocated;          // Total allocated
      -  size_t _wasted;             // of which wasted (internal fragmentation)
      -  size_t _undo_wasted;        // of which wasted on undo (is not used for calculation of PLAB size)
      -  size_t _unused;             // Unused in last buffer
      +  Atomic _allocated;          // Total allocated
      +  Atomic _wasted;             // of which wasted (internal fragmentation)
      +  Atomic _undo_wasted;        // of which wasted on undo (is not used for calculation of PLAB size)
      +  Atomic _unused;             // Unused in last buffer
       
         virtual void reset() {
      -    _allocated   = 0;
      -    _wasted      = 0;
      -    _undo_wasted = 0;
      -    _unused      = 0;
      +    _allocated.store_relaxed(0);
      +    _wasted.store_relaxed(0);
      +    _undo_wasted.store_relaxed(0);
      +    _unused.store_relaxed(0);
         }
       
       public:
      @@ -172,11 +173,11 @@ public:
       
         virtual ~PLABStats() { }
       
      -  size_t allocated() const { return _allocated; }
      -  size_t wasted() const { return _wasted; }
      -  size_t unused() const { return _unused; }
      +  size_t allocated() const { return _allocated.load_relaxed(); }
      +  size_t wasted() const { return _wasted.load_relaxed(); }
      +  size_t undo_wasted() const { return _undo_wasted.load_relaxed(); }
      +  size_t unused() const { return _unused.load_relaxed(); }
         size_t used() const { return allocated() - (wasted() + unused()); }
      -  size_t undo_wasted() const { return _undo_wasted; }
       
         static size_t min_size() {
           return PLAB::min_size();
      diff --git a/src/hotspot/share/gc/shared/plab.inline.hpp b/src/hotspot/share/gc/shared/plab.inline.hpp
      index 020738352d3..5f3e9c91e26 100644
      --- a/src/hotspot/share/gc/shared/plab.inline.hpp
      +++ b/src/hotspot/share/gc/shared/plab.inline.hpp
      @@ -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
      @@ -29,22 +29,21 @@
       
       #include "gc/shared/collectedHeap.inline.hpp"
       #include "memory/allocation.inline.hpp"
      -#include "runtime/atomicAccess.hpp"
       
       void PLABStats::add_allocated(size_t v) {
      -  AtomicAccess::add(&_allocated, v);
      +  _allocated.add_then_fetch(v);
       }
       
       void PLABStats::add_unused(size_t v) {
      -  AtomicAccess::add(&_unused, v);
      +  _unused.add_then_fetch(v);
       }
       
       void PLABStats::add_wasted(size_t v) {
      -  AtomicAccess::add(&_wasted, v);
      +  _wasted.add_then_fetch(v);
       }
       
       void PLABStats::add_undo_wasted(size_t v) {
      -  AtomicAccess::add(&_undo_wasted, v);
      +  _undo_wasted.add_then_fetch(v);
       }
       
       #endif // SHARE_GC_SHARED_PLAB_INLINE_HPP
      
      From ea90214ce90c916dd5145c09de6960f038843326 Mon Sep 17 00:00:00 2001
      From: Volkan Yazici 
      Date: Tue, 10 Feb 2026 09:01:28 +0000
      Subject: [PATCH 191/215] 8375352:
       java/net/httpclient/ConnectTimeoutWithProxy*.java tests fail on EC2
      
      Reviewed-by: dfuchs
      ---
       .../httpclient/AbstractConnectTimeout.java    | 258 ------------
       .../ConnectTimeoutNoProxyAsync.java           |  47 ---
       .../httpclient/ConnectTimeoutNoProxySync.java |  48 ---
       .../net/httpclient/ConnectTimeoutTest.java    | 370 ++++++++++++++++++
       .../ConnectTimeoutWithProxyAsync.java         |  47 ---
       .../ConnectTimeoutWithProxySync.java          |  48 ---
       6 files changed, 370 insertions(+), 448 deletions(-)
       delete mode 100644 test/jdk/java/net/httpclient/AbstractConnectTimeout.java
       delete mode 100644 test/jdk/java/net/httpclient/ConnectTimeoutNoProxyAsync.java
       delete mode 100644 test/jdk/java/net/httpclient/ConnectTimeoutNoProxySync.java
       create mode 100644 test/jdk/java/net/httpclient/ConnectTimeoutTest.java
       delete mode 100644 test/jdk/java/net/httpclient/ConnectTimeoutWithProxyAsync.java
       delete mode 100644 test/jdk/java/net/httpclient/ConnectTimeoutWithProxySync.java
      
      diff --git a/test/jdk/java/net/httpclient/AbstractConnectTimeout.java b/test/jdk/java/net/httpclient/AbstractConnectTimeout.java
      deleted file mode 100644
      index 2c490919181..00000000000
      --- a/test/jdk/java/net/httpclient/AbstractConnectTimeout.java
      +++ /dev/null
      @@ -1,258 +0,0 @@
      -/*
      - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * under the terms of the GNU General Public License version 2 only, as
      - * published by the Free Software Foundation.
      - *
      - * This code is distributed in the hope that it will be useful, but WITHOUT
      - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      - * version 2 for more details (a copy is included in the LICENSE file that
      - * accompanied this code).
      - *
      - * You should have received a copy of the GNU General Public License version
      - * 2 along with this work; if not, write to the Free Software Foundation,
      - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      - *
      - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      - * or visit www.oracle.com if you need additional information or have any
      - * questions.
      - */
      -
      -import java.net.ConnectException;
      -import java.net.InetSocketAddress;
      -import java.net.NoRouteToHostException;
      -import java.net.ProxySelector;
      -import java.net.URI;
      -import java.net.http.HttpClient;
      -import java.net.http.HttpClient.Version;
      -import java.net.http.HttpConnectTimeoutException;
      -import java.net.http.HttpRequest;
      -import java.net.http.HttpRequest.BodyPublishers;
      -import java.net.http.HttpResponse;
      -import java.net.http.HttpResponse.BodyHandlers;
      -import java.nio.channels.UnresolvedAddressException;
      -import java.time.Duration;
      -import java.util.ArrayList;
      -import java.util.Arrays;
      -import java.util.List;
      -import java.util.concurrent.CompletionException;
      -import org.testng.annotations.DataProvider;
      -import static java.lang.System.out;
      -import static java.net.http.HttpClient.Builder.NO_PROXY;
      -import static java.net.http.HttpClient.Version.HTTP_1_1;
      -import static java.net.http.HttpClient.Version.HTTP_2;
      -import static java.time.Duration.*;
      -import static java.util.concurrent.TimeUnit.NANOSECONDS;
      -import static org.testng.Assert.fail;
      -
      -public abstract class AbstractConnectTimeout {
      -
      -    static final Duration NO_DURATION = null;
      -
      -    static List> TIMEOUTS = List.of(
      -                    // connectTimeout   HttpRequest timeout
      -            Arrays.asList( NO_DURATION,   ofMillis(100) ),
      -            Arrays.asList( NO_DURATION,   ofNanos(1)    ),
      -
      -            Arrays.asList( ofMillis(100), NO_DURATION   ),
      -            Arrays.asList( ofNanos(1),    NO_DURATION   ),
      -
      -            Arrays.asList( ofMillis(100), ofMinutes(1)  ),
      -            Arrays.asList( ofNanos(1),    ofMinutes(1)  )
      -    );
      -
      -    static final List METHODS = List.of("GET", "POST");
      -    static final List VERSIONS = List.of(HTTP_2, HTTP_1_1);
      -    static final List SCHEMES = List.of("https", "http");
      -
      -    @DataProvider(name = "variants")
      -    public Object[][] variants() {
      -        List l = new ArrayList<>();
      -        for (List timeouts : TIMEOUTS) {
      -           Duration connectTimeout = timeouts.get(0);
      -           Duration requestTimeout = timeouts.get(1);
      -           for (String method: METHODS) {
      -            for (String scheme : SCHEMES) {
      -             for (Version requestVersion : VERSIONS) {
      -              l.add(new Object[] {requestVersion, scheme, method, connectTimeout, requestTimeout});
      -        }}}}
      -        return l.stream().toArray(Object[][]::new);
      -    }
      -
      -    static final ProxySelector EXAMPLE_DOT_COM_PROXY = ProxySelector.of(
      -            InetSocketAddress.createUnresolved("example.com", 8080));
      -
      -    //@Test(dataProvider = "variants")
      -    protected void timeoutNoProxySync(Version requestVersion,
      -                                      String scheme,
      -                                      String method,
      -                                      Duration connectTimeout,
      -                                      Duration requestTimeout)
      -        throws Exception
      -    {
      -        timeoutSync(requestVersion, scheme, method, connectTimeout, requestTimeout, NO_PROXY);
      -    }
      -
      -    //@Test(dataProvider = "variants")
      -    protected void timeoutWithProxySync(Version requestVersion,
      -                                        String scheme,
      -                                        String method,
      -                                        Duration connectTimeout,
      -                                        Duration requestTimeout)
      -        throws Exception
      -    {
      -        timeoutSync(requestVersion, scheme, method, connectTimeout, requestTimeout, EXAMPLE_DOT_COM_PROXY);
      -    }
      -
      -    private void timeoutSync(Version requestVersion,
      -                             String scheme,
      -                             String method,
      -                             Duration connectTimeout,
      -                             Duration requestTimeout,
      -                             ProxySelector proxy)
      -        throws Exception
      -    {
      -        out.printf("%ntimeoutSync(requestVersion=%s, scheme=%s, method=%s,"
      -                   + " connectTimeout=%s, requestTimeout=%s, proxy=%s)%n",
      -                   requestVersion, scheme, method, connectTimeout, requestTimeout, proxy);
      -
      -        HttpClient client = newClient(connectTimeout, proxy);
      -        HttpRequest request = newRequest(scheme, requestVersion, method, requestTimeout);
      -
      -        for (int i = 0; i < 2; i++) {
      -            out.printf("iteration %d%n", i);
      -            long startTime = System.nanoTime();
      -            try {
      -                HttpResponse resp = client.send(request, BodyHandlers.ofString());
      -                printResponse(resp);
      -                fail("Unexpected response: " + resp);
      -            } catch (HttpConnectTimeoutException expected) { // blocking thread-specific exception
      -                long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
      -                out.printf("Client: received in %d millis%n", elapsedTime);
      -                assertExceptionTypeAndCause(expected.getCause());
      -            } catch (ConnectException e) {
      -                long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
      -                out.printf("Client: received in %d millis%n", elapsedTime);
      -                Throwable t = e.getCause().getCause();  // blocking thread-specific exception
      -                if (!isAcceptableCause(t)) { // tolerate only NRTHE or UAE
      -                    e.printStackTrace(out);
      -                    fail("Unexpected exception:" + e);
      -                } else {
      -                    out.printf("Caught ConnectException with "
      -                            + " cause: %s - skipping%n", t.getCause());
      -                }
      -            }
      -        }
      -    }
      -
      -    //@Test(dataProvider = "variants")
      -    protected void timeoutNoProxyAsync(Version requestVersion,
      -                                       String scheme,
      -                                       String method,
      -                                       Duration connectTimeout,
      -                                       Duration requestTimeout) {
      -        timeoutAsync(requestVersion, scheme, method, connectTimeout, requestTimeout, NO_PROXY);
      -    }
      -
      -    //@Test(dataProvider = "variants")
      -    protected void timeoutWithProxyAsync(Version requestVersion,
      -                                         String scheme,
      -                                         String method,
      -                                         Duration connectTimeout,
      -                                         Duration requestTimeout) {
      -        timeoutAsync(requestVersion, scheme, method, connectTimeout, requestTimeout, EXAMPLE_DOT_COM_PROXY);
      -    }
      -
      -    private void timeoutAsync(Version requestVersion,
      -                              String scheme,
      -                              String method,
      -                              Duration connectTimeout,
      -                              Duration requestTimeout,
      -                              ProxySelector proxy) {
      -        out.printf("%ntimeoutAsync(requestVersion=%s, scheme=%s, method=%s, "
      -                   + "connectTimeout=%s, requestTimeout=%s, proxy=%s)%n",
      -                   requestVersion, scheme, method, connectTimeout, requestTimeout, proxy);
      -
      -        HttpClient client = newClient(connectTimeout, proxy);
      -        HttpRequest request = newRequest(scheme, requestVersion, method, requestTimeout);
      -        for (int i = 0; i < 2; i++) {
      -            out.printf("iteration %d%n", i);
      -            long startTime = System.nanoTime();
      -            try {
      -                HttpResponse resp = client.sendAsync(request, BodyHandlers.ofString()).join();
      -                printResponse(resp);
      -                fail("Unexpected response: " + resp);
      -            } catch (CompletionException e) {
      -                long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
      -                out.printf("Client: received in %d millis%n", elapsedTime);
      -                Throwable t = e.getCause();
      -                if (t instanceof ConnectException && isAcceptableCause(t.getCause())) {
      -                    // tolerate only NRTHE and UAE
      -                    out.printf("Caught ConnectException with "
      -                            + "cause: %s - skipping%n", t.getCause());
      -                } else {
      -                    assertExceptionTypeAndCause(t);
      -                }
      -            }
      -        }
      -    }
      -
      -    static boolean isAcceptableCause(Throwable cause) {
      -        if (cause instanceof NoRouteToHostException) return true;
      -        if (cause instanceof UnresolvedAddressException) return true;
      -        return false;
      -    }
      -
      -    static HttpClient newClient(Duration connectTimeout, ProxySelector proxy) {
      -        HttpClient.Builder builder = HttpClient.newBuilder().proxy(proxy);
      -        if (connectTimeout != NO_DURATION)
      -            builder.connectTimeout(connectTimeout);
      -        return builder.build();
      -    }
      -
      -    static HttpRequest newRequest(String scheme,
      -                                  Version reqVersion,
      -                                  String method,
      -                                  Duration requestTimeout) {
      -        // Resolvable address. Most tested environments just ignore the TCP SYN,
      -        // or occasionally return ICMP no route to host
      -        URI uri = URI.create(scheme +"://example.com:81/");
      -        HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(uri);
      -        reqBuilder = reqBuilder.version(reqVersion);
      -        switch (method) {
      -            case "GET"   : reqBuilder.GET();                         break;
      -            case "POST"  : reqBuilder.POST(BodyPublishers.noBody()); break;
      -            default: throw new AssertionError("Unknown method:" + method);
      -        }
      -        if (requestTimeout != NO_DURATION)
      -            reqBuilder.timeout(requestTimeout);
      -        return reqBuilder.build();
      -    }
      -
      -    static void assertExceptionTypeAndCause(Throwable t) {
      -        if (!(t instanceof HttpConnectTimeoutException)) {
      -            t.printStackTrace(out);
      -            fail("Expected HttpConnectTimeoutException, got:" + t);
      -        }
      -        Throwable connEx = t.getCause();
      -        if (!(connEx instanceof ConnectException)) {
      -            t.printStackTrace(out);
      -            fail("Expected ConnectException cause in:" + connEx);
      -        }
      -        out.printf("Caught expected HttpConnectTimeoutException with ConnectException"
      -                + " cause: %n%s%n%s%n", t, connEx);
      -        final String EXPECTED_MESSAGE = "HTTP connect timed out"; // impl dependent
      -        if (!connEx.getMessage().equals(EXPECTED_MESSAGE))
      -            fail("Expected: \"" + EXPECTED_MESSAGE + "\", got: \"" + connEx.getMessage() + "\"");
      -
      -    }
      -
      -    static void printResponse(HttpResponse response) {
      -        out.println("Unexpected response: " + response);
      -        out.println("Headers: " + response.headers());
      -        out.println("Body: " + response.body());
      -    }
      -}
      diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutNoProxyAsync.java b/test/jdk/java/net/httpclient/ConnectTimeoutNoProxyAsync.java
      deleted file mode 100644
      index ace12cd0295..00000000000
      --- a/test/jdk/java/net/httpclient/ConnectTimeoutNoProxyAsync.java
      +++ /dev/null
      @@ -1,47 +0,0 @@
      -/*
      - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * under the terms of the GNU General Public License version 2 only, as
      - * published by the Free Software Foundation.
      - *
      - * This code is distributed in the hope that it will be useful, but WITHOUT
      - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      - * version 2 for more details (a copy is included in the LICENSE file that
      - * accompanied this code).
      - *
      - * You should have received a copy of the GNU General Public License version
      - * 2 along with this work; if not, write to the Free Software Foundation,
      - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      - *
      - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      - * or visit www.oracle.com if you need additional information or have any
      - * questions.
      - */
      -
      -import java.net.http.HttpClient.Version;
      -import java.time.Duration;
      -import org.testng.annotations.Test;
      -
      -/*
      - * @test
      - * @summary Tests for connection related timeouts
      - * @bug 8208391
      - * @run testng/othervm ConnectTimeoutNoProxyAsync
      - */
      -
      -public class ConnectTimeoutNoProxyAsync extends AbstractConnectTimeout {
      -
      -    @Test(dataProvider = "variants")
      -    @Override
      -    public void timeoutNoProxyAsync(Version requestVersion,
      -                                    String scheme,
      -                                    String method,
      -                                    Duration connectTimeout,
      -                                    Duration requestduration)
      -    {
      -        super.timeoutNoProxyAsync(requestVersion, scheme, method, connectTimeout, requestduration);
      -    }
      -}
      diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutNoProxySync.java b/test/jdk/java/net/httpclient/ConnectTimeoutNoProxySync.java
      deleted file mode 100644
      index f30dea6deea..00000000000
      --- a/test/jdk/java/net/httpclient/ConnectTimeoutNoProxySync.java
      +++ /dev/null
      @@ -1,48 +0,0 @@
      -/*
      - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * under the terms of the GNU General Public License version 2 only, as
      - * published by the Free Software Foundation.
      - *
      - * This code is distributed in the hope that it will be useful, but WITHOUT
      - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      - * version 2 for more details (a copy is included in the LICENSE file that
      - * accompanied this code).
      - *
      - * You should have received a copy of the GNU General Public License version
      - * 2 along with this work; if not, write to the Free Software Foundation,
      - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      - *
      - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      - * or visit www.oracle.com if you need additional information or have any
      - * questions.
      - */
      -
      -import java.net.http.HttpClient.Version;
      -import java.time.Duration;
      -import org.testng.annotations.Test;
      -
      -/*
      - * @test
      - * @summary Tests for connection related timeouts
      - * @bug 8208391
      - * @run testng/othervm ConnectTimeoutNoProxySync
      - */
      -
      -public class ConnectTimeoutNoProxySync extends AbstractConnectTimeout {
      -
      -    @Test(dataProvider = "variants")
      -    @Override
      -    public void timeoutNoProxySync(Version requestVersion,
      -                                   String scheme,
      -                                   String method,
      -                                   Duration connectTimeout,
      -                                   Duration requestTimeout)
      -        throws Exception
      -    {
      -        super.timeoutNoProxySync(requestVersion, scheme, method, connectTimeout, requestTimeout);
      -    }
      -}
      diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutTest.java b/test/jdk/java/net/httpclient/ConnectTimeoutTest.java
      new file mode 100644
      index 00000000000..c837071ae7c
      --- /dev/null
      +++ b/test/jdk/java/net/httpclient/ConnectTimeoutTest.java
      @@ -0,0 +1,370 @@
      +/*
      + * 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
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      + * or visit www.oracle.com if you need additional information or have any
      + * questions.
      + */
      +
      +import jdk.test.lib.Utils;
      +import org.junit.jupiter.api.AfterAll;
      +import org.junit.jupiter.params.ParameterizedTest;
      +import org.junit.jupiter.params.provider.MethodSource;
      +
      +import java.io.IOException;
      +import java.io.PrintStream;
      +import java.net.ConnectException;
      +import java.net.InetAddress;
      +import java.net.Proxy;
      +import java.net.ProxySelector;
      +import java.net.ServerSocket;
      +import java.net.Socket;
      +import java.net.SocketAddress;
      +import java.net.SocketTimeoutException;
      +import java.net.URI;
      +import java.net.http.HttpClient;
      +import java.net.http.HttpClient.Version;
      +import java.net.http.HttpConnectTimeoutException;
      +import java.net.http.HttpRequest;
      +import java.net.http.HttpRequest.BodyPublishers;
      +import java.net.http.HttpResponse;
      +import java.net.http.HttpResponse.BodyHandlers;
      +import java.time.Duration;
      +import java.util.ArrayList;
      +import java.util.Arrays;
      +import java.util.List;
      +import java.util.concurrent.CompletionException;
      +import java.util.stream.Stream;
      +
      +import static java.lang.Boolean.parseBoolean;
      +import static java.net.http.HttpClient.Builder.NO_PROXY;
      +import static java.net.http.HttpClient.Version.HTTP_1_1;
      +import static java.net.http.HttpClient.Version.HTTP_2;
      +import static java.time.Duration.*;
      +import static java.util.concurrent.TimeUnit.NANOSECONDS;
      +import static org.junit.jupiter.api.Assertions.fail;
      +
      +/*
      + * @test id=sync
      + * @bug 8208391 8375352
      + * @summary Verifies behavior on `connect()` timeouts
      + * @requires os.family != "windows"
      + * @library /test/lib
      + * @run junit/othervm ${test.main.class}
      + */
      +
      +/*
      + * @test id=sync-proxy
      + * @bug 8208391 8375352
      + * @summary Verifies behavior on `connect()` timeouts
      + * @requires os.family != "windows"
      + * @library /test/lib
      + * @run junit/othervm -Dtest.proxy=true ${test.main.class}
      + */
      +
      +/*
      + * @test id=async
      + * @bug 8208391 8375352
      + * @summary Verifies behavior on `connect()` timeouts
      + * @requires os.family != "windows"
      + * @library /test/lib
      + * @run junit/othervm -Dtest.async=true ${test.main.class}
      + */
      +
      +/*
      + * @test id=async-proxy
      + * @bug 8208391 8375352
      + * @summary Verifies behavior on `connect()` timeouts
      + * @requires os.family != "windows"
      + * @library /test/lib
      + * @run junit/othervm -Dtest.async=true -Dtest.proxy=true ${test.main.class}
      + */
      +
      +class ConnectTimeoutTest {
      +
      +    // This test verifies the `HttpClient` behavior on `connect()` failures.
      +    //
      +    // Earlier, the test was trying to connect `example.com:8080` to trigger a `connect()` failure.
      +    // This worked, until it doesn't — `example.com:8080` started responding in certain test environments.
      +    //
      +    // Now we create a `ServerSocket` and exhaust all its "SYN backlog" and "Accept queue".
      +    // The expectation is that the platform socket in this state will block on `connect()`.
      +    // Well... It doesn't on Windows, whereas it does on Linux and macOS.
      +    // Windows doesn't block and immediately responds with `java.net.ConnectException: Connection refused: connect`.
      +    // Neither it is deterministic how many connections are needed to exhaust a socket admission queue.
      +    // Hence, we took the following decisions:
      +    //
      +    // 1. Skip this test on Windows
      +    // 2. Exhaust server socket admission queue by going into a loop
      +
      +    private static final PrintStream LOGGER = System.out;
      +
      +    private static final int BACKLOG = 1;
      +
      +    /**
      +     * A {@link ServerSocket} whose admission will be blocked by exhausting all its "SYN backlog" and "Accept queue".
      +     */
      +    private static final ServerSocket SERVER_SOCKET = createServerSocket();
      +
      +    /**
      +     * Client sockets exhausting the admission to {@link #SERVER_SOCKET}.
      +     */
      +    private static final List CLIENT_SOCKETS = createClientSocketsExhaustingServerSocketAdmission();
      +
      +    private static ServerSocket createServerSocket() {
      +        try {
      +            LOGGER.println("Creating server socket");
      +            return new ServerSocket(0, BACKLOG, InetAddress.getLoopbackAddress());
      +        } catch (Exception exception) {
      +            throw new RuntimeException(exception);
      +        }
      +    }
      +
      +    private static List createClientSocketsExhaustingServerSocketAdmission() {
      +        List sockets = new ArrayList<>();
      +        int maxSocketCount = BACKLOG   // To fill up the backlog
      +                + 512;                 // Giving some slack, should be enough to exhaust the admission queue.
      +        int connectTimeout = Math.toIntExact(Math.addExact(500, Utils.adjustTimeout(500)));
      +        int socketIndex = 0;
      +        for (; socketIndex < maxSocketCount; socketIndex++) {
      +            try {
      +                LOGGER.printf(
      +                        "Creating client socket %s/%s to exhaust the server socket admission%n",
      +                        (socketIndex + 1), maxSocketCount);
      +                Socket socket = new Socket();
      +                socket.connect(SERVER_SOCKET.getLocalSocketAddress(), connectTimeout);
      +                sockets.add(socket);
      +            } catch (ConnectException | SocketTimeoutException exception) {
      +                LOGGER.printf(
      +                        "Received expected `%s` while creating client socket %s/%s%n",
      +                        exception.getClass().getName(), (socketIndex + 1), maxSocketCount);
      +                return sockets;
      +            } catch (IOException ioe) {
      +                String message = String.format(
      +                        "Received unexpected exception while creating client socket %s/%s",
      +                        (socketIndex + 1), maxSocketCount);
      +                closeSockets(SERVER_SOCKET, sockets);
      +                throw new RuntimeException(message, ioe);
      +            }
      +        }
      +        String message = String.format(
      +                "Connected %s sockets, but still could not exhaust the socket admission",
      +                maxSocketCount);
      +        closeSockets(SERVER_SOCKET, sockets);
      +        throw new RuntimeException(message);
      +    }
      +
      +    @AfterAll
      +    public static void closeSockets() {
      +        closeSockets(SERVER_SOCKET, CLIENT_SOCKETS);
      +    }
      +
      +    private static void closeSockets(ServerSocket serverSocket, List clientSockets) {
      +        Throwable[] throwable = {null};
      +        Stream.concat(clientSockets.stream(), Stream.of(serverSocket)).forEach(closeable -> {
      +            try {
      +                closeable.close();
      +            } catch (Exception exception) {
      +                if (throwable[0] == null) {
      +                    throwable[0] = exception;
      +                } else {
      +                    throwable[0].addSuppressed(exception);
      +                }
      +            }
      +        });
      +        if (throwable[0] != null) {
      +            throwable[0].printStackTrace(System.out);
      +        }
      +    }
      +
      +    /**
      +     * {@link ProxySelector} always pointing to {@link #SERVER_SOCKET}.
      +     */
      +    private static final ProxySelector PROXY_SELECTOR = new ProxySelector() {
      +
      +        private static final List PROXIES =
      +                List.of(new Proxy(Proxy.Type.HTTP, SERVER_SOCKET.getLocalSocketAddress()));
      +
      +        @Override
      +        public List select(URI uri) {
      +            return PROXIES;
      +        }
      +
      +        @Override
      +        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
      +            // Do nothing
      +        }
      +
      +    };
      +
      +    private static final Duration NO_DURATION = null;
      +
      +    private static List> TIMEOUTS = List.of(
      +                    // connectTimeout   HttpRequest timeout
      +            Arrays.asList( NO_DURATION,   ofMillis(100) ),
      +            Arrays.asList( NO_DURATION,   ofNanos(1)    ),
      +
      +            Arrays.asList( ofMillis(100), NO_DURATION   ),
      +            Arrays.asList( ofNanos(1),    NO_DURATION   ),
      +
      +            Arrays.asList( ofMillis(100), ofMinutes(1)  ),
      +            Arrays.asList( ofNanos(1),    ofMinutes(1)  )
      +    );
      +
      +    private static final List METHODS = List.of("GET", "POST");
      +    private static final List VERSIONS = List.of(HTTP_2, HTTP_1_1);
      +    private static final List SCHEMES = List.of("https", "http");
      +
      +    static Object[][] variants() {
      +        List l = new ArrayList<>();
      +        for (List timeouts : TIMEOUTS) {
      +           Duration connectTimeout = timeouts.get(0);
      +           Duration requestTimeout = timeouts.get(1);
      +           for (String method: METHODS) {
      +            for (String scheme : SCHEMES) {
      +             for (Version requestVersion : VERSIONS) {
      +              l.add(new Object[] {requestVersion, scheme, method, connectTimeout, requestTimeout});
      +        }}}}
      +        return l.stream().toArray(Object[][]::new);
      +    }
      +
      +    @ParameterizedTest
      +    @MethodSource("variants")
      +    void test(
      +            Version requestVersion,
      +            String scheme,
      +            String method,
      +            Duration connectTimeout,
      +            Duration requestTimeout)
      +            throws Exception {
      +        ProxySelector proxySelector = parseBoolean(System.getProperty("test.proxy")) ? PROXY_SELECTOR : NO_PROXY;
      +        boolean async = parseBoolean(System.getProperty("test.async"));
      +        if (async) {
      +            timeoutAsync(requestVersion, scheme, method, connectTimeout, requestTimeout, proxySelector);
      +        } else {
      +            timeoutSync(requestVersion, scheme, method, connectTimeout, requestTimeout, proxySelector);
      +        }
      +    }
      +
      +    private void timeoutSync(Version requestVersion,
      +                             String scheme,
      +                             String method,
      +                             Duration connectTimeout,
      +                             Duration requestTimeout,
      +                             ProxySelector proxy)
      +        throws Exception
      +    {
      +        HttpClient client = newClient(connectTimeout, proxy);
      +        HttpRequest request = newRequest(scheme, requestVersion, method, requestTimeout);
      +
      +        for (int i = 0; i < 2; i++) {
      +            LOGGER.printf("iteration %d%n", i);
      +            long startTime = System.nanoTime();
      +            try {
      +                HttpResponse resp = client.send(request, BodyHandlers.ofString());
      +                printResponse(resp);
      +                fail("Unexpected response: " + resp);
      +            } catch (HttpConnectTimeoutException expected) { // blocking thread-specific exception
      +                long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
      +                LOGGER.printf("Client: received in %d millis%n", elapsedTime);
      +                assertExceptionTypeAndCause(expected.getCause());
      +            } catch (ConnectException e) {
      +                long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
      +                LOGGER.printf("Client: received in %d millis%n", elapsedTime);
      +                Throwable t = e.getCause().getCause();  // blocking thread-specific exception
      +                e.printStackTrace(LOGGER);
      +                fail("Unexpected exception:" + e);
      +            }
      +        }
      +    }
      +
      +    private void timeoutAsync(Version requestVersion,
      +                              String scheme,
      +                              String method,
      +                              Duration connectTimeout,
      +                              Duration requestTimeout,
      +                              ProxySelector proxy) {
      +        HttpClient client = newClient(connectTimeout, proxy);
      +        HttpRequest request = newRequest(scheme, requestVersion, method, requestTimeout);
      +        for (int i = 0; i < 2; i++) {
      +            LOGGER.printf("iteration %d%n", i);
      +            long startTime = System.nanoTime();
      +            try {
      +                HttpResponse resp = client.sendAsync(request, BodyHandlers.ofString()).join();
      +                printResponse(resp);
      +                fail("Unexpected response: " + resp);
      +            } catch (CompletionException e) {
      +                long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
      +                LOGGER.printf("Client: received in %d millis%n", elapsedTime);
      +                Throwable t = e.getCause();
      +                assertExceptionTypeAndCause(t);
      +            }
      +        }
      +    }
      +
      +    private static HttpClient newClient(Duration connectTimeout, ProxySelector proxy) {
      +        HttpClient.Builder builder = HttpClient.newBuilder().proxy(proxy);
      +        if (connectTimeout != NO_DURATION)
      +            builder.connectTimeout(connectTimeout);
      +        return builder.build();
      +    }
      +
      +    private static HttpRequest newRequest(String scheme,
      +                                  Version reqVersion,
      +                                  String method,
      +                                  Duration requestTimeout) {
      +        String hostAddress = SERVER_SOCKET.getInetAddress().getHostAddress();
      +        int hostPort = SERVER_SOCKET.getLocalPort();
      +        URI uri = URI.create(scheme + "://" + hostAddress + ':' + hostPort);
      +        HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(uri);
      +        reqBuilder = reqBuilder.version(reqVersion);
      +        switch (method) {
      +            case "GET"   : reqBuilder.GET();                         break;
      +            case "POST"  : reqBuilder.POST(BodyPublishers.noBody()); break;
      +            default: throw new AssertionError("Unknown method:" + method);
      +        }
      +        if (requestTimeout != NO_DURATION)
      +            reqBuilder.timeout(requestTimeout);
      +        return reqBuilder.build();
      +    }
      +
      +    private static void assertExceptionTypeAndCause(Throwable t) {
      +        if (!(t instanceof HttpConnectTimeoutException)) {
      +            t.printStackTrace(LOGGER);
      +            fail("Expected HttpConnectTimeoutException, got:" + t);
      +        }
      +        Throwable connEx = t.getCause();
      +        if (!(connEx instanceof ConnectException)) {
      +            t.printStackTrace(LOGGER);
      +            fail("Expected ConnectException cause in:" + connEx);
      +        }
      +        LOGGER.printf("Caught expected HttpConnectTimeoutException with ConnectException"
      +                + " cause: %n%s%n%s%n", t, connEx);
      +        final String EXPECTED_MESSAGE = "HTTP connect timed out"; // impl dependent
      +        if (!connEx.getMessage().equals(EXPECTED_MESSAGE))
      +            fail("Expected: \"" + EXPECTED_MESSAGE + "\", got: \"" + connEx.getMessage() + "\"");
      +
      +    }
      +
      +    private static void printResponse(HttpResponse response) {
      +        LOGGER.println("Unexpected response: " + response);
      +        LOGGER.println("Headers: " + response.headers());
      +        LOGGER.println("Body: " + response.body());
      +    }
      +
      +}
      diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutWithProxyAsync.java b/test/jdk/java/net/httpclient/ConnectTimeoutWithProxyAsync.java
      deleted file mode 100644
      index a6e0c22c15a..00000000000
      --- a/test/jdk/java/net/httpclient/ConnectTimeoutWithProxyAsync.java
      +++ /dev/null
      @@ -1,47 +0,0 @@
      -/*
      - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * under the terms of the GNU General Public License version 2 only, as
      - * published by the Free Software Foundation.
      - *
      - * This code is distributed in the hope that it will be useful, but WITHOUT
      - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      - * version 2 for more details (a copy is included in the LICENSE file that
      - * accompanied this code).
      - *
      - * You should have received a copy of the GNU General Public License version
      - * 2 along with this work; if not, write to the Free Software Foundation,
      - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      - *
      - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      - * or visit www.oracle.com if you need additional information or have any
      - * questions.
      - */
      -
      -import java.net.http.HttpClient.Version;
      -import java.time.Duration;
      -import org.testng.annotations.Test;
      -
      -/*
      - * @test
      - * @summary Tests for connection related timeouts
      - * @bug 8208391
      - * @run testng/othervm ConnectTimeoutWithProxyAsync
      - */
      -
      -public class ConnectTimeoutWithProxyAsync extends AbstractConnectTimeout {
      -
      -    @Test(dataProvider = "variants")
      -    @Override
      -    public void timeoutWithProxyAsync(Version requestVersion,
      -                                      String scheme,
      -                                      String method,
      -                                      Duration connectTimeout,
      -                                      Duration requestTimeout)
      -    {
      -        super.timeoutWithProxyAsync(requestVersion, scheme, method, connectTimeout, requestTimeout);
      -    }
      -}
      diff --git a/test/jdk/java/net/httpclient/ConnectTimeoutWithProxySync.java b/test/jdk/java/net/httpclient/ConnectTimeoutWithProxySync.java
      deleted file mode 100644
      index a61fdc48bcf..00000000000
      --- a/test/jdk/java/net/httpclient/ConnectTimeoutWithProxySync.java
      +++ /dev/null
      @@ -1,48 +0,0 @@
      -/*
      - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
      - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      - *
      - * This code is free software; you can redistribute it and/or modify it
      - * under the terms of the GNU General Public License version 2 only, as
      - * published by the Free Software Foundation.
      - *
      - * This code is distributed in the hope that it will be useful, but WITHOUT
      - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      - * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      - * version 2 for more details (a copy is included in the LICENSE file that
      - * accompanied this code).
      - *
      - * You should have received a copy of the GNU General Public License version
      - * 2 along with this work; if not, write to the Free Software Foundation,
      - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      - *
      - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      - * or visit www.oracle.com if you need additional information or have any
      - * questions.
      - */
      -
      -import java.net.http.HttpClient.Version;
      -import java.time.Duration;
      -import org.testng.annotations.Test;
      -
      -/*
      - * @test
      - * @summary Tests for connection related timeouts
      - * @bug 8208391
      - * @run testng/othervm ConnectTimeoutWithProxySync
      - */
      -
      -public class ConnectTimeoutWithProxySync extends AbstractConnectTimeout {
      -
      -    @Test(dataProvider = "variants")
      -    @Override
      -    public void timeoutWithProxySync(Version requestVersion,
      -                                     String scheme,
      -                                     String method,
      -                                     Duration connectTimeout,
      -                                     Duration requestTimeout)
      -        throws Exception
      -    {
      -        super.timeoutWithProxySync(requestVersion, scheme, method, connectTimeout, requestTimeout);
      -    }
      -}
      
      From 665dc490c2a1bcaa1fa1cf6f0ea2396ce4b31863 Mon Sep 17 00:00:00 2001
      From: Liam Miller-Cushon 
      Date: Tue, 10 Feb 2026 09:08:54 +0000
      Subject: [PATCH 192/215] 8208752: Calling a deserialized Lambda might fail
       with ClassCastException 8374654: Inconsistent handling of lambda
       deserialization for Object method references on interfaces
      
      Reviewed-by: vromero, jlahoda
      ---
       .../com/sun/tools/javac/code/Types.java       | 10 ++-
       .../sun/tools/javac/comp/LambdaToMethod.java  | 41 +++++----
       .../tools/javac/resources/compiler.properties |  5 +-
       .../LambdaSerializedClassCastException.java   | 63 ++++++++++++++
       .../LambdaSerializedClassCastException.out    |  2 +
       ...bleObjectMethodReferencesOnInterfaces.java | 84 +++++++++++++++++++
       ...ableObjectMethodReferencesOnInterfaces.out |  4 +
       .../lambda/SerializableObjectMethods.out      |  4 +-
       8 files changed, 188 insertions(+), 25 deletions(-)
       create mode 100644 test/langtools/tools/javac/lambda/LambdaSerializedClassCastException.java
       create mode 100644 test/langtools/tools/javac/lambda/LambdaSerializedClassCastException.out
       create mode 100644 test/langtools/tools/javac/lambda/SerializableObjectMethodReferencesOnInterfaces.java
       create mode 100644 test/langtools/tools/javac/lambda/SerializableObjectMethodReferencesOnInterfaces.out
      
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
      index 3f3eb1f9623..539b1470a75 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
      @@ -2858,13 +2858,17 @@ public class Types {
                   hasSameArgs(t, erasure(s)) || hasSameArgs(erasure(t), s);
           }
       
      -    public boolean overridesObjectMethod(TypeSymbol origin, Symbol msym) {
      +    public Symbol overriddenObjectMethod(TypeSymbol origin, Symbol msym) {
               for (Symbol sym : syms.objectType.tsym.members().getSymbolsByName(msym.name)) {
                   if (msym.overrides(sym, origin, Types.this, true)) {
      -                return true;
      +                return sym;
                   }
               }
      -        return false;
      +        return null;
      +    }
      +
      +    public boolean overridesObjectMethod(TypeSymbol origin, Symbol msym) {
      +        return overriddenObjectMethod(origin, msym) != null;
           }
       
           /**
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      index 7d0c5192039..40628709dfe 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
      @@ -701,22 +701,24 @@ public class LambdaToMethod extends TreeTranslator {
                       rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.nil()));
           }
       
      -    private void addDeserializationCase(MethodHandleSymbol refSym, Type targetType, MethodSymbol samSym,
      +    private void addDeserializationCase(MethodHandleSymbol refSym, Type targetType, MethodSymbol samSym, Type samType,
                                               DiagnosticPosition pos, List staticArgs, MethodType indyType) {
               String functionalInterfaceClass = classSig(targetType);
               String functionalInterfaceMethodName = samSym.getSimpleName().toString();
               String functionalInterfaceMethodSignature = typeSig(types.erasure(samSym.type));
      -        Symbol baseMethod = refSym.baseSymbol();
      -        Symbol origMethod = baseMethod.baseSymbol();
      -        if (baseMethod != origMethod && origMethod.owner == syms.objectType.tsym) {
      -            //the implementation method is a java.lang.Object method transferred to an
      -            //interface that does not declare it. Runtime will refer to this method as to
      -            //a java.lang.Object method, so do the same:
      -            refSym = ((MethodSymbol) origMethod).asHandle();
      +        if (refSym.enclClass().isInterface()) {
      +            Symbol baseMethod = types.overriddenObjectMethod(refSym.enclClass(), refSym);
      +            if (baseMethod != null) {
      +                // The implementation method is a java.lang.Object method, runtime will resolve this method to
      +                // a java.lang.Object method, so do the same.
      +                // This case can be removed if JDK-8172817 is fixed.
      +                refSym = ((MethodSymbol) baseMethod).asHandle();
      +            }
               }
               String implClass = classSig(types.erasure(refSym.owner.type));
               String implMethodName = refSym.getQualifiedName().toString();
               String implMethodSignature = typeSig(types.erasure(refSym.type));
      +        String instantiatedMethodType = typeSig(types.erasure(samType));
       
               int implMethodKind = refSym.referenceKind();
               JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType),
      @@ -730,13 +732,14 @@ public class LambdaToMethod extends TreeTranslator {
                   ++i;
               }
               JCStatement stmt = make.If(
      -                deserTest(deserTest(deserTest(deserTest(deserTest(
      -                                                        kindTest,
      -                                                        "getFunctionalInterfaceClass", functionalInterfaceClass),
      -                                                "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
      -                                        "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
      -                                "getImplClass", implClass),
      -                        "getImplMethodSignature", implMethodSignature),
      +                deserTest(deserTest(deserTest(deserTest(deserTest(deserTest(
      +                                                                kindTest,
      +                                                                "getFunctionalInterfaceClass", functionalInterfaceClass),
      +                                                        "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
      +                                                "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
      +                                        "getImplClass", implClass),
      +                                "getImplMethodSignature", implMethodSignature),
      +                        "getInstantiatedMethodType", instantiatedMethodType),
                       make.Return(makeIndyCall(
                               pos,
                               syms.lambdaMetafactory,
      @@ -756,7 +759,8 @@ public class LambdaToMethod extends TreeTranslator {
                           implMethodKind,
                           implClass,
                           implMethodName,
      -                    implMethodSignature));
      +                    implMethodSignature,
      +                    instantiatedMethodType));
               }
               stmts.append(stmt);
           }
      @@ -818,10 +822,11 @@ public class LambdaToMethod extends TreeTranslator {
                                                        List indy_args) {
               //determine the static bsm args
               MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.target.tsym);
      +        MethodType samType = typeToMethodType(tree.getDescriptorType(types));
               List staticArgs = List.of(
                       typeToMethodType(samSym.type),
                       refSym.asHandle(),
      -                typeToMethodType(tree.getDescriptorType(types)));
      +                samType);
       
               //computed indy arg types
               ListBuffer indy_args_types = new ListBuffer<>();
      @@ -885,7 +890,7 @@ public class LambdaToMethod extends TreeTranslator {
                       int prevPos = make.pos;
                       try {
                           make.at(kInfo.clazz);
      -                    addDeserializationCase(refSym, tree.type, samSym,
      +                    addDeserializationCase(refSym, tree.type, samSym, samType,
                                   tree, staticArgs, indyType);
                       } finally {
                           make.at(prevPos);
      diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
      index bb81916becb..915d7f8a8d8 100644
      --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
      +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
      @@ -1721,7 +1721,7 @@ compiler.note.mref.stat.1=\
           alternate metafactory = {0}\n\
           bridge method = {1}
       
      -# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string
      +# 0: string, 1: string, 2: string, 3: number, 4: string, 5: string, 6: string, 7: string
       compiler.note.lambda.deserialization.stat=\
           Generating lambda deserialization\n\
           functionalInterfaceClass: {0}\n\
      @@ -1730,7 +1730,8 @@ compiler.note.lambda.deserialization.stat=\
           implMethodKind: {3}\n\
           implClass: {4}\n\
           implMethodName: {5}\n\
      -    implMethodSignature: {6}
      +    implMethodSignature: {6}\n\
      +    instantiatedMethodType: {7}
       
       compiler.note.note=\
           Note:\u0020
      diff --git a/test/langtools/tools/javac/lambda/LambdaSerializedClassCastException.java b/test/langtools/tools/javac/lambda/LambdaSerializedClassCastException.java
      new file mode 100644
      index 00000000000..f63f4b64e79
      --- /dev/null
      +++ b/test/langtools/tools/javac/lambda/LambdaSerializedClassCastException.java
      @@ -0,0 +1,63 @@
      +/*
      + * Copyright (c) 2024, Alphabet LLC. All rights reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please 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 8208752
      + * @summary NPE generating serializedLambdaName for nested lambda
      + * @compile/ref=LambdaSerializedClassCastException.out -XDrawDiagnostics --debug=dumpLambdaDeserializationStats LambdaSerializedClassCastException.java
      + * @run main LambdaSerializedClassCastException
      + */
      +
      +import java.io.ByteArrayInputStream;
      +import java.io.ByteArrayOutputStream;
      +import java.io.ObjectInputStream;
      +import java.io.ObjectOutputStream;
      +import java.io.Serializable;
      +import java.util.function.Function;
      +
      +public class LambdaSerializedClassCastException {
      +
      +    public static void main(String[] args) throws Exception {
      +
      +        Function lambda1 =
      +                (Function & Serializable) Object::toString;
      +        Function lambda2 =
      +                (Function & Serializable) Object::toString;
      +
      +        Function deserial = serialDeserial(lambda2);
      +        deserial.apply(new Object());
      +    }
      +
      +    @SuppressWarnings("unchecked")
      +    static  T serialDeserial(T object) throws Exception {
      +        ByteArrayOutputStream baos = new ByteArrayOutputStream();
      +        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
      +            oos.writeObject(object);
      +        }
      +        try (ObjectInputStream ois =
      +                new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
      +            return (T) ois.readObject();
      +        }
      +    }
      +}
      diff --git a/test/langtools/tools/javac/lambda/LambdaSerializedClassCastException.out b/test/langtools/tools/javac/lambda/LambdaSerializedClassCastException.out
      new file mode 100644
      index 00000000000..4ad61e5e00e
      --- /dev/null
      +++ b/test/langtools/tools/javac/lambda/LambdaSerializedClassCastException.out
      @@ -0,0 +1,2 @@
      +LambdaSerializedClassCastException.java:44:59: compiler.note.lambda.deserialization.stat: java/util/function/Function, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, toString, ()Ljava/lang/String;, (Ljava/lang/String;)Ljava/lang/String;
      +LambdaSerializedClassCastException.java:46:59: compiler.note.lambda.deserialization.stat: java/util/function/Function, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, toString, ()Ljava/lang/String;, (Ljava/lang/Object;)Ljava/lang/String;
      diff --git a/test/langtools/tools/javac/lambda/SerializableObjectMethodReferencesOnInterfaces.java b/test/langtools/tools/javac/lambda/SerializableObjectMethodReferencesOnInterfaces.java
      new file mode 100644
      index 00000000000..18d732fb0dd
      --- /dev/null
      +++ b/test/langtools/tools/javac/lambda/SerializableObjectMethodReferencesOnInterfaces.java
      @@ -0,0 +1,84 @@
      +/*
      + * Copyright (c) 2026, Google LLC and/or its affiliates. All rights reserved.
      + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      + *
      + * This code is free software; you can redistribute it and/or modify it
      + * under the terms of the GNU General Public License version 2 only, as
      + * published by the Free Software Foundation.
      + *
      + * This code is distributed in the hope that it will be useful, but WITHOUT
      + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      + * version 2 for more details (a copy is included in the LICENSE file that
      + * accompanied this code).
      + *
      + * You should have received a copy of the GNU General Public License version
      + * 2 along with this work; if not, write to the Free Software Foundation,
      + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      + *
      + * Please 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 8374654 8208752
      + * @summary test lambda deserialization for Object method references on interfaces
      + * @compile/ref=SerializableObjectMethodReferencesOnInterfaces.out -XDrawDiagnostics --debug=dumpLambdaDeserializationStats SerializableObjectMethodReferencesOnInterfaces.java
      + * @run main SerializableObjectMethodReferencesOnInterfaces
      + */
      +
      +import java.io.ByteArrayInputStream;
      +import java.io.ByteArrayOutputStream;
      +import java.io.ObjectInputStream;
      +import java.io.ObjectOutputStream;
      +import java.io.Serializable;
      +
      +public class SerializableObjectMethodReferencesOnInterfaces {
      +
      +    public static void main(String[] args) throws Exception {
      +        new Test().run();
      +    }
      +
      +    static class Test {
      +        interface I1 extends Serializable {}
      +
      +        interface I2 extends I1 {
      +            @Override
      +            public int hashCode();
      +        }
      +
      +        interface F extends Serializable {
      +            R apply(T t);
      +        }
      +
      +        enum E {
      +            ONE
      +        }
      +
      +        void run() throws Exception {
      +            F f1 = I1::hashCode;
      +            F f2 = I2::hashCode;
      +            F f3 = E::hashCode;
      +            F f4 = Object::hashCode;
      +
      +            serialDeserial(f1).apply(new I1() {});
      +            serialDeserial(f2).apply(new I2() {});
      +            serialDeserial(f3).apply(E.ONE);
      +            serialDeserial(f4).apply(new Object());
      +        }
      +    }
      +
      +    @SuppressWarnings("unchecked")
      +    static  T serialDeserial(T object) throws Exception {
      +        ByteArrayOutputStream baos = new ByteArrayOutputStream();
      +        try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
      +            oos.writeObject(object);
      +        }
      +        try (ObjectInputStream ois =
      +                new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
      +            return (T) ois.readObject();
      +        }
      +    }
      +}
      diff --git a/test/langtools/tools/javac/lambda/SerializableObjectMethodReferencesOnInterfaces.out b/test/langtools/tools/javac/lambda/SerializableObjectMethodReferencesOnInterfaces.out
      new file mode 100644
      index 00000000000..657235aa6fe
      --- /dev/null
      +++ b/test/langtools/tools/javac/lambda/SerializableObjectMethodReferencesOnInterfaces.out
      @@ -0,0 +1,4 @@
      +SerializableObjectMethodReferencesOnInterfaces.java:61:33: compiler.note.lambda.deserialization.stat: SerializableObjectMethodReferencesOnInterfaces$Test$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, hashCode, ()I, (LSerializableObjectMethodReferencesOnInterfaces$Test$I1;)Ljava/lang/Integer;
      +SerializableObjectMethodReferencesOnInterfaces.java:62:33: compiler.note.lambda.deserialization.stat: SerializableObjectMethodReferencesOnInterfaces$Test$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, hashCode, ()I, (LSerializableObjectMethodReferencesOnInterfaces$Test$I2;)Ljava/lang/Integer;
      +SerializableObjectMethodReferencesOnInterfaces.java:63:32: compiler.note.lambda.deserialization.stat: SerializableObjectMethodReferencesOnInterfaces$Test$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Enum, hashCode, ()I, (LSerializableObjectMethodReferencesOnInterfaces$Test$E;)Ljava/lang/Integer;
      +SerializableObjectMethodReferencesOnInterfaces.java:64:37: compiler.note.lambda.deserialization.stat: SerializableObjectMethodReferencesOnInterfaces$Test$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, hashCode, ()I, (Ljava/lang/Object;)Ljava/lang/Integer;
      diff --git a/test/langtools/tools/javac/lambda/SerializableObjectMethods.out b/test/langtools/tools/javac/lambda/SerializableObjectMethods.out
      index d03cc145794..56caaabc6de 100644
      --- a/test/langtools/tools/javac/lambda/SerializableObjectMethods.out
      +++ b/test/langtools/tools/javac/lambda/SerializableObjectMethods.out
      @@ -1,4 +1,4 @@
      -SerializableObjectMethods.java:59:35: compiler.note.lambda.deserialization.stat: SerializableObjectMethods$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, hashCode, ()I
      -SerializableObjectMethods.java:60:35: compiler.note.lambda.deserialization.stat: SerializableObjectMethods$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 9, SerializableObjectMethods$I2, hashCode, ()I
      +SerializableObjectMethods.java:59:35: compiler.note.lambda.deserialization.stat: SerializableObjectMethods$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, hashCode, ()I, (LSerializableObjectMethods$I1;)Ljava/lang/Integer;
      +SerializableObjectMethods.java:60:35: compiler.note.lambda.deserialization.stat: SerializableObjectMethods$F, apply, (Ljava/lang/Object;)Ljava/lang/Object;, 5, java/lang/Object, hashCode, ()I, (LSerializableObjectMethods$I2;)Ljava/lang/Integer;
       - compiler.note.unchecked.filename: SerializableObjectMethods.java
       - compiler.note.unchecked.recompile
      
      From bd2a3b80625e49b77d9500e793276e26bb7d8028 Mon Sep 17 00:00:00 2001
      From: Ramkumar Sunderbabu 
      Date: Tue, 10 Feb 2026 09:28:46 +0000
      Subject: [PATCH 193/215] 8373040: Mark
       compiler/codecache/CodeCacheSegmentSizeTest.java as flagless
      
      Reviewed-by: syan, chagedorn
      ---
       .../jtreg/compiler/codecache/CodeCacheSegmentSizeTest.java     | 3 ++-
       1 file changed, 2 insertions(+), 1 deletion(-)
      
      diff --git a/test/hotspot/jtreg/compiler/codecache/CodeCacheSegmentSizeTest.java b/test/hotspot/jtreg/compiler/codecache/CodeCacheSegmentSizeTest.java
      index 5531bdd1517..b6d051fcd06 100644
      --- a/test/hotspot/jtreg/compiler/codecache/CodeCacheSegmentSizeTest.java
      +++ b/test/hotspot/jtreg/compiler/codecache/CodeCacheSegmentSizeTest.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2025 IBM Corporation. All rights reserved.
      + * Copyright (c) 2025, 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
      @@ -28,6 +28,7 @@
        *          - fails gracefully for invalid value
        *          - succeeds for valid value
        * @library /test/lib
      + * @requires vm.flagless
        * @run driver CodeCacheSegmentSizeTest
        */
       
      
      From bfb6de5b2b5870d4bd964e5aab791ef88270c5a4 Mon Sep 17 00:00:00 2001
      From: Afshin Zafari 
      Date: Tue, 10 Feb 2026 10:07:39 +0000
      Subject: [PATCH 194/215] 8377429: Warning as error in asan build in
       test_nmt_cornercases.cpp on Linux
      
      Reviewed-by: dholmes, syan
      ---
       test/hotspot/gtest/nmt/test_nmt_cornercases.cpp | 8 ++++----
       1 file changed, 4 insertions(+), 4 deletions(-)
      
      diff --git a/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp b/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp
      index 24ab9888521..e0a452b1831 100644
      --- a/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp
      +++ b/test/hotspot/gtest/nmt/test_nmt_cornercases.cpp
      @@ -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
      @@ -31,6 +31,9 @@
       #include "testutils.hpp"
       #include "unittest.hpp"
       
      +// ASAN complains about allocating very large sizes
      +#if !INCLUDE_ASAN
      +
       // Check NMT header for integrity, as well as expected type and size.
       static void check_expected_malloc_header(const void* payload, MemTag mem_tag, size_t size) {
         const MallocHeader* hdr = MallocHeader::resolve_checked(payload);
      @@ -38,9 +41,6 @@ static void check_expected_malloc_header(const void* payload, MemTag mem_tag, si
         EXPECT_EQ(hdr->mem_tag(), mem_tag);
       }
       
      -// ASAN complains about allocating very large sizes
      -#if !INCLUDE_ASAN
      -
       // Check that a malloc with an overflowing size is rejected.
       TEST_VM(NMT, malloc_failure1) {
         void* p = os::malloc(SIZE_MAX, mtTest);
      
      From 264fdc5b4ed5f4e35168048533196e670c3dda6c Mon Sep 17 00:00:00 2001
      From: Mikhail Yankelevich 
      Date: Tue, 10 Feb 2026 12:18:03 +0000
      Subject: [PATCH 195/215] 8374808: Add new methods to KeyStore and KeyStoreSpi
       that return the creation date as an Instant instead of Date
      
      Reviewed-by: weijun
      ---
       .../classes/apple/security/KeychainStore.java | 47 +++++++++----
       .../com/sun/crypto/provider/JceKeyStore.java  | 67 ++++++++++++-------
       .../share/classes/java/security/KeyStore.java | 32 ++++++++-
       .../classes/java/security/KeyStoreSpi.java    | 26 ++++++-
       .../sun/security/pkcs12/PKCS12KeyStore.java   | 58 +++++++++++-----
       .../sun/security/provider/DomainKeyStore.java | 27 ++++++--
       .../sun/security/provider/JavaKeyStore.java   | 48 +++++++++----
       .../security/KeyStore/TestKeyStoreBasic.java  | 31 ++++++++-
       .../security/provider/KeyStore/DKSTest.java   | 44 ++++++++----
       9 files changed, 291 insertions(+), 89 deletions(-)
      
      diff --git a/src/java.base/macosx/classes/apple/security/KeychainStore.java b/src/java.base/macosx/classes/apple/security/KeychainStore.java
      index 6f70fccbb24..63dfc39afe1 100644
      --- a/src/java.base/macosx/classes/apple/security/KeychainStore.java
      +++ b/src/java.base/macosx/classes/apple/security/KeychainStore.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 2011, 2024, 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
      @@ -30,6 +30,8 @@ import java.security.*;
       import java.security.cert.*;
       import java.security.cert.Certificate;
       import java.security.spec.*;
      +import java.time.Instant;
      +import java.time.temporal.ChronoUnit;
       import java.util.*;
       
       import javax.crypto.*;
      @@ -114,7 +116,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
           // SecCertificateRef.  When we delete the key we have to delete all of the corresponding
           // native objects.
           static class KeyEntry {
      -        Date date; // the creation date of this entry
      +        Instant date; // the creation instant of this entry
               byte[] protectedPrivKey;
               char[] password;
               long keyRef;  // SecKeyRef for this key
      @@ -124,7 +126,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
       
           // Trusted certificates
           static class TrustedCertEntry {
      -        Date date; // the creation date of this entry
      +        Instant date; // the creation instant of this entry
       
               Certificate cert;
               long certRef;  // SecCertificateRef for this key
      @@ -405,13 +407,32 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
            * not exist
            */
           public Date engineGetCreationDate(String alias) {
      -        Object entry = entries.get(alias.toLowerCase(Locale.ROOT));
      +        final Instant instant = this.engineGetCreationInstant(alias);
      +        if (instant == null) {
      +            return null;
      +        }
      +        return Date.from(instant);
      +    }
      +
      +    /**
      +     * Returns the instant that the entry identified by the given alias was
      +     * created.
      +     *
      +     * @param alias the alias name
      +     *
      +     * @return the instant that the entry identified by the given alias
      +     * was created, or {@code null} if the given alias does not exist
      +     *
      +     * @since 27
      +     */
      +    public Instant engineGetCreationInstant(String alias) {
      +        final Object entry = entries.get(alias.toLowerCase(Locale.ROOT));
       
               if (entry != null) {
      -            if (entry instanceof TrustedCertEntry) {
      -                return new Date(((TrustedCertEntry)entry).date.getTime());
      +            if (entry instanceof TrustedCertEntry trustedCertEntry) {
      +                return trustedCertEntry.date;
                   } else {
      -                return new Date(((KeyEntry)entry).date.getTime());
      +                return ((KeyEntry)entry).date;
                   }
               } else {
                   return null;
      @@ -447,7 +468,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
               synchronized(entries) {
                   try {
                       KeyEntry entry = new KeyEntry();
      -                entry.date = new Date();
      +                entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
       
                       if (key instanceof PrivateKey) {
                           if ((key.getFormat().equals("PKCS#8")) ||
      @@ -525,7 +546,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
                                                   + "EncryptedPrivateKeyInfo");
                   }
       
      -            entry.date = new Date();
      +            entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
       
                   if ((chain != null) &&
                       (chain.length != 0)) {
      @@ -927,9 +948,9 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
       
                   // Make a creation date.
                   if (creationDate != 0)
      -                tce.date = new Date(creationDate);
      +                tce.date = Instant.ofEpochMilli(creationDate);
                   else
      -                tce.date = new Date();
      +                tce.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
       
                   entries.put(alias.toLowerCase(Locale.ROOT), tce);
               } catch (Exception e) {
      @@ -952,9 +973,9 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
       
               // Make a creation date.
               if (creationDate != 0)
      -            ke.date = new Date(creationDate);
      +            ke.date = Instant.ofEpochMilli(creationDate);
               else
      -            ke.date = new Date();
      +            ke.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
       
               // Next, create X.509 Certificate objects from the raw data.  This is complicated
               // because a certificate's public key may be too long for Java's default encryption strength.
      diff --git a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java
      index ad98653b9c2..1e45a4e6a20 100644
      --- a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java
      +++ b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java
      @@ -1,5 +1,5 @@
       /*
      - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
      + * Copyright (c) 1998, 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,8 @@ import sun.security.util.Debug;
       import sun.security.util.IOUtils;
       
       import java.io.*;
      +import java.time.Instant;
      +import java.time.temporal.ChronoUnit;
       import java.util.*;
       import java.security.DigestInputStream;
       import java.security.DigestOutputStream;
      @@ -70,14 +72,14 @@ public final class JceKeyStore extends KeyStoreSpi {
       
           // Private key and supporting certificate chain
           private static final class PrivateKeyEntry {
      -        Date date; // the creation date of this entry
      +        Instant date; // the creation date of this entry
               byte[] protectedKey;
               Certificate[] chain;
           }
       
           // Secret key
           private static final class SecretKeyEntry {
      -        Date date; // the creation date of this entry
      +        Instant date; // the creation date of this entry
               SealedObject sealedKey;
       
               // Maximum possible length of sealedKey. Used to detect malicious
      @@ -89,7 +91,7 @@ public final class JceKeyStore extends KeyStoreSpi {
       
           // Trusted certificate
           private static final class TrustedCertEntry {
      -        Date date; // the creation date of this entry
      +        Instant date; // the creation date of this entry
               Certificate cert;
           }
       
      @@ -213,23 +215,38 @@ public final class JceKeyStore extends KeyStoreSpi {
            * not exist
            */
           public Date engineGetCreationDate(String alias) {
      -        Date date = null;
      +        final Instant instant = this.engineGetCreationInstant(alias);
      +        if (instant == null) {
      +            return null;
      +        }
      +        return Date.from(instant);
      +    }
       
      -        Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
      +    /**
      +     * Returns the instant that the entry identified by the given alias was
      +     * created.
      +     *
      +     * @param alias the alias name
      +     *
      +     * @return the instant that the entry identified by the given alias
      +     * was created, or {@code null} if the given alias does not exist
      +     *
      +     * @since 27
      +     */
      +    public Instant engineGetCreationInstant(String alias) {
      +        final Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
       
               if (entry != null) {
      -            // We have to create a new instance of java.util.Date because
      -            // dates are not immutable
                   if (entry instanceof TrustedCertEntry) {
      -                date = new Date(((TrustedCertEntry)entry).date.getTime());
      +                return ((TrustedCertEntry)entry).date;
                   } else if (entry instanceof PrivateKeyEntry) {
      -                date = new Date(((PrivateKeyEntry)entry).date.getTime());
      +                return ((PrivateKeyEntry)entry).date;
                   } else {
      -                date = new Date(((SecretKeyEntry)entry).date.getTime());
      +                return ((SecretKeyEntry)entry).date;
                   }
      +        } else {
      +            return null;
               }
      -
      -        return date;
           }
       
           /**
      @@ -264,7 +281,7 @@ public final class JceKeyStore extends KeyStoreSpi {
       
                       if (key instanceof PrivateKey) {
                           PrivateKeyEntry entry = new PrivateKeyEntry();
      -                    entry.date = new Date();
      +                    entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
       
                           // protect the private key
                           entry.protectedKey = keyProtector.protect((PrivateKey)key);
      @@ -282,7 +299,7 @@ public final class JceKeyStore extends KeyStoreSpi {
       
                       } else {
                           SecretKeyEntry entry = new SecretKeyEntry();
      -                    entry.date = new Date();
      +                    entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
       
                           // seal and store the key
                           entry.sealedKey = keyProtector.seal(key);
      @@ -325,7 +342,7 @@ public final class JceKeyStore extends KeyStoreSpi {
                   // We assume it's a private key, because there is no standard
                   // (ASN.1) encoding format for wrapped secret keys
                   PrivateKeyEntry entry = new PrivateKeyEntry();
      -            entry.date = new Date();
      +            entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
       
                   entry.protectedKey = key.clone();
                   if ((chain != null) &&
      @@ -370,7 +387,7 @@ public final class JceKeyStore extends KeyStoreSpi {
       
                   TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
                   trustedCertEntry.cert = cert;
      -            trustedCertEntry.date = new Date();
      +            trustedCertEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
                   entries.put(alias.toLowerCase(Locale.ENGLISH), trustedCertEntry);
               }
           }
      @@ -588,7 +605,7 @@ public final class JceKeyStore extends KeyStoreSpi {
                               dos.writeUTF(alias);
       
                               // write the (entry creation) date
      -                        dos.writeLong(pentry.date.getTime());
      +                        dos.writeLong(pentry.date.toEpochMilli());
       
                               // write the protected private key
                               dos.writeInt(pentry.protectedKey.length);
      @@ -618,7 +635,9 @@ public final class JceKeyStore extends KeyStoreSpi {
                               dos.writeUTF(alias);
       
                               // write the (entry creation) date
      -                        dos.writeLong(((TrustedCertEntry)entry).date.getTime());
      +                        dos.writeLong(
      +                                ((TrustedCertEntry)entry).date.toEpochMilli()
      +                        );
       
                               // write the trusted certificate
                               encoded = ((TrustedCertEntry)entry).cert.getEncoded();
      @@ -635,7 +654,9 @@ public final class JceKeyStore extends KeyStoreSpi {
                               dos.writeUTF(alias);
       
                               // write the (entry creation) date
      -                        dos.writeLong(((SecretKeyEntry)entry).date.getTime());
      +                        dos.writeLong(
      +                                ((SecretKeyEntry)entry).date.toEpochMilli()
      +                        );
       
                               // write the sealed key
                               oos = new ObjectOutputStream(dos);
      @@ -753,7 +774,7 @@ public final class JceKeyStore extends KeyStoreSpi {
                               alias = dis.readUTF();
       
                               // read the (entry creation) date
      -                        entry.date = new Date(dis.readLong());
      +                        entry.date = Instant.ofEpochMilli(dis.readLong());
       
                               // read the private key
                               entry.protectedKey = IOUtils.readExactlyNBytes(dis, dis.readInt());
      @@ -798,7 +819,7 @@ public final class JceKeyStore extends KeyStoreSpi {
                               alias = dis.readUTF();
       
                               // read the (entry creation) date
      -                        entry.date = new Date(dis.readLong());
      +                        entry.date = Instant.ofEpochMilli(dis.readLong());
       
                               // read the trusted certificate
                               if (xVersion == 2) {
      @@ -832,7 +853,7 @@ public final class JceKeyStore extends KeyStoreSpi {
                               alias = dis.readUTF();
       
                               // read the (entry creation) date
      -                        entry.date = new Date(dis.readLong());
      +                        entry.date = Instant.ofEpochMilli(dis.readLong());
       
                               // read the sealed key
                               try {
      diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java
      index 8f3d4ba29fd..83414082cab 100644
      --- a/src/java.base/share/classes/java/security/KeyStore.java
      +++ b/src/java.base/share/classes/java/security/KeyStore.java
      @@ -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
      @@ -30,6 +30,7 @@ import java.security.cert.Certificate;
       import java.security.cert.X509Certificate;
       import java.security.cert.CertificateException;
       import java.security.spec.AlgorithmParameterSpec;
      +import java.time.Instant;
       import java.util.*;
       import javax.crypto.SecretKey;
       
      @@ -1182,6 +1183,9 @@ public class KeyStore {
       
           /**
            * Returns the creation date of the entry identified by the given alias.
      +     * 

      + * It is recommended to use the {@link #getCreationInstant(String)} + * method instead. * * @param alias the alias name * @@ -1200,6 +1204,32 @@ public class KeyStore { return keyStoreSpi.engineGetCreationDate(alias); } + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @throws KeyStoreException if the keystore has not been initialized + * (loaded) + * + * @since 27 + */ + public final Instant getCreationInstant(String alias) + throws KeyStoreException + { + if (!initialized) { + throw new KeyStoreException("Uninitialized keystore"); + } + return keyStoreSpi.engineGetCreationInstant(alias); + } + + + /** * Assigns the given key to the given alias, protecting it with the given * password. diff --git a/src/java.base/share/classes/java/security/KeyStoreSpi.java b/src/java.base/share/classes/java/security/KeyStoreSpi.java index 0b0aa8adf19..5c60f44e4e9 100644 --- a/src/java.base/share/classes/java/security/KeyStoreSpi.java +++ b/src/java.base/share/classes/java/security/KeyStoreSpi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 java.security; import java.io.*; +import java.time.Instant; import java.util.*; import java.security.KeyStore.*; @@ -127,6 +128,29 @@ public abstract class KeyStoreSpi { */ public abstract Date engineGetCreationDate(String alias); + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @apiNote Subclasses should override this method to directly return an + * instant. + * + * @implSpec + * The default implementation calls {@code engineGetCreationDate(alias)} + * and returns the output as an {@code Instant} value. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { + final Date date = engineGetCreationDate(alias); + return date == null ? null : date.toInstant(); + } + /** * Assigns the given key to the given alias, protecting it with the given * password. diff --git a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index f85ba0f9c4b..33e8406a7f6 100644 --- a/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.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 @@ -35,6 +35,8 @@ import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -179,7 +181,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { // A keystore entry and associated attributes private static class Entry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry String alias; byte[] keyId; Set attributes; @@ -212,7 +214,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { CertEntry(X509Certificate cert, byte[] keyId, String alias, ObjectIdentifier[] trustedKeyUsage, Set attributes) { - this.date = new Date(); + this.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); this.cert = cert; this.keyId = keyId; this.alias = alias; @@ -541,9 +543,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { - Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + final Instant instant = this.engineGetCreationInstant(alias); + if (instant == null) { + return null; + } + return Date.from(instant); + } + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { + final Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); if (entry != null) { - return new Date(entry.date.getTime()); + return entry.date; } else { return null; } @@ -606,7 +627,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { checkX509Certs(chain); PrivateKeyEntry keyEntry = new PrivateKeyEntry(); - keyEntry.date = new Date(); + keyEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); if ((key.getFormat().equals("PKCS#8")) || (key.getFormat().equals("PKCS8"))) { @@ -651,7 +672,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } else if (key instanceof SecretKey) { SecretKeyEntry keyEntry = new SecretKeyEntry(); - keyEntry.date = new Date(); + keyEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); // Encode secret key in a PKCS#8 DerOutputStream secretKeyInfo = new DerOutputStream(); @@ -690,7 +711,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { entry.attributes.addAll(attributes); } // set the keyId to current date - entry.keyId = ("Time " + (entry.date).getTime()).getBytes(UTF_8); + entry.keyId = ("Time " + entry.date.toEpochMilli()).getBytes(UTF_8); // set the alias entry.alias = alias.toLowerCase(Locale.ENGLISH); // add the entry @@ -745,7 +766,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } PrivateKeyEntry entry = new PrivateKeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); if (debug != null) { debug.println("Setting a protected private key at alias '" + @@ -753,7 +774,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } // set the keyId to current date - entry.keyId = ("Time " + (entry.date).getTime()).getBytes(UTF_8); + entry.keyId = ("Time " + entry.date.toEpochMilli()).getBytes(UTF_8); // set the alias entry.alias = alias.toLowerCase(Locale.ENGLISH); @@ -2411,21 +2432,22 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } } entry.keyId = keyId; - // restore date if it exists + // restore instant if it exists String keyIdStr = new String(keyId, UTF_8); - Date date = null; + Instant instant = null; if (keyIdStr.startsWith("Time ")) { try { - date = new Date( - Long.parseLong(keyIdStr.substring(5))); + instant = Instant.ofEpochMilli( + Long.parseLong(keyIdStr.substring(5)) + ); } catch (Exception e) { - // date has been initialized to null + // instant has been initialized to null } } - if (date == null) { - date = new Date(); + if (instant == null) { + instant = Instant.now().truncatedTo(ChronoUnit.MILLIS); } - entry.date = date; + entry.date = instant; if (bagItem instanceof PrivateKeyEntry) { keyList.add(entry); diff --git a/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java b/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java index 342df6c5eeb..163454b55fc 100644 --- a/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java +++ b/src/java.base/share/classes/sun/security/provider/DomainKeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, 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 @@ -30,6 +30,7 @@ import java.net.*; import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.time.Instant; import java.util.*; import static java.nio.charset.StandardCharsets.UTF_8; @@ -214,16 +215,32 @@ abstract class DomainKeyStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { + final Instant instant = this.engineGetCreationInstant(alias); + return (instant == null) ? null : Date.from(instant); + } + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { AbstractMap.SimpleEntry> pair = getKeystoresForReading(alias); - Date date = null; + Instant instant = null; try { String entryAlias = pair.getKey(); for (KeyStore keystore : pair.getValue()) { - date = keystore.getCreationDate(entryAlias); - if (date != null) { + instant = keystore.getCreationInstant(entryAlias); + if (instant != null) { break; } } @@ -231,7 +248,7 @@ abstract class DomainKeyStore extends KeyStoreSpi { throw new IllegalStateException(e); } - return date; + return instant; } @Override diff --git a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java index 73ca0c6bf16..f014c2ecbc0 100644 --- a/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java +++ b/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java @@ -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 @@ -30,6 +30,8 @@ import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.CertificateException; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import static java.nio.charset.StandardCharsets.UTF_8; @@ -100,14 +102,14 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { // Private keys and their supporting certificate chains private static class KeyEntry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry byte[] protectedPrivKey; Certificate[] chain; } // Trusted certificates private static class TrustedCertEntry { - Date date; // the creation date of this entry + Instant date; // the creation date of this entry Certificate cert; } @@ -236,13 +238,31 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { - Object entry = entries.get(convertAlias(alias)); + final Instant instant = this.engineGetCreationInstant(alias); + return instant == null ? null : Date.from(instant); + } + + + + /** + * Returns the instant that the entry identified by the given alias was + * created. + * + * @param alias the alias name + * + * @return the instant that the entry identified by the given alias + * was created, or {@code null} if the given alias does not exist + * + * @since 27 + */ + public Instant engineGetCreationInstant(String alias) { + final Object entry = entries.get(convertAlias(alias)); if (entry != null) { if (entry instanceof TrustedCertEntry) { - return new Date(((TrustedCertEntry)entry).date.getTime()); + return ((TrustedCertEntry)entry).date; } else { - return new Date(((KeyEntry)entry).date.getTime()); + return((KeyEntry)entry).date; } } else { return null; @@ -287,7 +307,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { try { synchronized(entries) { KeyEntry entry = new KeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); // Protect the encoding of the key passwordBytes = convertToBytes(password); @@ -350,7 +370,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { } KeyEntry entry = new KeyEntry(); - entry.date = new Date(); + entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); entry.protectedPrivKey = key.clone(); if ((chain != null) && @@ -391,7 +411,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { TrustedCertEntry trustedCertEntry = new TrustedCertEntry(); trustedCertEntry.cert = cert; - trustedCertEntry.date = new Date(); + trustedCertEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS); entries.put(convertAlias(alias), trustedCertEntry); } } @@ -579,7 +599,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { dos.writeUTF(alias); // Write the (entry creation) date - dos.writeLong(((KeyEntry)entry).date.getTime()); + dos.writeLong(((KeyEntry)entry).date.toEpochMilli()); // Write the protected private key dos.writeInt(((KeyEntry)entry).protectedPrivKey.length); @@ -608,7 +628,9 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { dos.writeUTF(alias); // Write the (entry creation) date - dos.writeLong(((TrustedCertEntry)entry).date.getTime()); + dos.writeLong( + ((TrustedCertEntry)entry).date.toEpochMilli() + ); // Write the trusted certificate encoded = ((TrustedCertEntry)entry).cert.getEncoded(); @@ -707,7 +729,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { alias = dis.readUTF(); // Read the (entry creation) date - entry.date = new Date(dis.readLong()); + entry.date = Instant.ofEpochMilli(dis.readLong()); // Read the private key entry.protectedPrivKey = @@ -756,7 +778,7 @@ public abstract sealed class JavaKeyStore extends KeyStoreSpi { alias = dis.readUTF(); // Read the (entry creation) date - entry.date = new Date(dis.readLong()); + entry.date = Instant.ofEpochMilli(dis.readLong()); // Read the trusted certificate if (xVersion == 2) { diff --git a/test/jdk/java/security/KeyStore/TestKeyStoreBasic.java b/test/jdk/java/security/KeyStore/TestKeyStoreBasic.java index 8218c9cb48a..e39793cf1b7 100644 --- a/test/jdk/java/security/KeyStore/TestKeyStoreBasic.java +++ b/test/jdk/java/security/KeyStore/TestKeyStoreBasic.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 @@ -24,13 +24,18 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.security.*; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.PEMDecoder; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; +import java.time.Instant; /* * @test - * @bug 8048621 8133090 8167371 8236671 + * @bug 8048621 8133090 8167371 8236671 8374808 * @enablePreview * @summary Test basic operations with keystores (jks, jceks, pkcs12) * @author Yu-Ching Valerie PENG @@ -177,6 +182,8 @@ public class TestKeyStoreBasic { // compare the creation date of the 2 key stores for all aliases compareCreationDate(ks, ks2, numEntries); + compareCreationInstant(ks, ks2, numEntries); + // remove the last entry from the 2nd key store numEntries--; ks2.deleteEntry(ALIAS_HEAD + numEntries); @@ -213,6 +220,7 @@ public class TestKeyStoreBasic { // compare the creation date of the 2 key stores for all aliases compareCreationDate(ks, ks2, numEntries); + compareCreationInstant(ks, ks2, numEntries); // check setEntry/getEntry with a password protection algorithm if ("PKCS12".equalsIgnoreCase(ks.getType())) { @@ -284,6 +292,23 @@ public class TestKeyStoreBasic { } } + // compare the creation instants - true if all the same + private void compareCreationInstant(KeyStore o1, KeyStore o2, int range) + throws KeyStoreException { + String alias; + for (int k = 0; k < range; k++) { + alias = ALIAS_HEAD + k; + final Instant instant1 = o1.getCreationInstant(alias); + final Instant instant2 = o2.getCreationInstant(alias); + if (!(instant1.equals(instant2))) { + throw new RuntimeException("ERROR: entry creation time (" + k + + ") differs Instants {" + + instant1 + " - " + + instant2 + "}"); + } + } + } + // checks if an exception was caused by specified exception class private static boolean causedBy(Exception e, Class klass) { Throwable cause = e; diff --git a/test/jdk/sun/security/provider/KeyStore/DKSTest.java b/test/jdk/sun/security/provider/KeyStore/DKSTest.java index 9c35a1c1dd9..ee655f5a2a6 100644 --- a/test/jdk/sun/security/provider/KeyStore/DKSTest.java +++ b/test/jdk/sun/security/provider/KeyStore/DKSTest.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. * 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,19 +23,30 @@ /* * @test - * @bug 8007755 + * @bug 8007755 8374808 * @library /test/lib * @summary Support the logical grouping of keystores */ -import java.io.*; -import java.net.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; import java.nio.file.Paths; -import java.security.*; +import java.security.DomainLoadStoreParameter; import java.security.KeyStore; -import java.security.cert.*; +import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; -import java.util.*; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; @@ -205,17 +216,26 @@ public class DKSTest { new KeyStore.TrustedCertificateEntry(cert), null); } - private static void checkEntries(KeyStore keystore, int expected) + private static void checkEntries(KeyStore keystore, int expectedCount) throws Exception { - int i = 0; + int currCount = 0; for (String alias : Collections.list(keystore.aliases())) { System.out.print("."); - i++; + currCount++; + + // check creation date and instant + if(!keystore.getCreationDate(alias).equals( + Date.from(keystore.getCreationInstant(alias))) + ){ + throw new RuntimeException( + "Creation Date is not the same as Instant timestamp"); + } } System.out.println(); - if (expected != i) { + // Check if current count is expected + if (expectedCount != currCount) { throw new Exception("Error: unexpected entry count in keystore: " + - "loaded=" + i + ", expected=" + expected); + "loaded=" + currCount + ", expected=" + expectedCount); } } From 28f845a670f767f43acfdb1c6de287003c93c53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20Sikstr=C3=B6m?= Date: Tue, 10 Feb 2026 13:19:40 +0000 Subject: [PATCH 196/215] 8376777: Consistent use of nonstatic instead of non_static in ci files Reviewed-by: chagedorn, aseoane --- src/hotspot/share/ci/ciField.cpp | 4 ++-- src/hotspot/share/ci/ciInstanceKlass.cpp | 6 +++--- src/hotspot/share/ci/ciInstanceKlass.hpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp index e0c818f02fc..946fea5346f 100644 --- a/src/hotspot/share/ci/ciField.cpp +++ b/src/hotspot/share/ci/ciField.cpp @@ -213,7 +213,7 @@ ciField::ciField(fieldDescriptor *fd) : "bootstrap classes must not create & cache unshared fields"); } -static bool trust_final_non_static_fields(ciInstanceKlass* holder) { +static bool trust_final_nonstatic_fields(ciInstanceKlass* holder) { if (holder == nullptr) return false; if (holder->trust_final_fields()) { @@ -259,7 +259,7 @@ void ciField::initialize_from(fieldDescriptor* fd) { // An instance field can be constant if it's a final static field or if // it's a final non-static field of a trusted class (classes in // java.lang.invoke and sun.invoke packages and subpackages). - _is_constant = is_stable_field || trust_final_non_static_fields(_holder); + _is_constant = is_stable_field || trust_final_nonstatic_fields(_holder); } } else { // For CallSite objects treat the target field as a compile time constant. diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp index 33bcabc4566..6243258acd9 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.cpp +++ b/src/hotspot/share/ci/ciInstanceKlass.cpp @@ -392,7 +392,7 @@ bool ciInstanceKlass::contains_field_offset(int offset) { return get_instanceKlass()->contains_field_offset(offset); } -ciField* ciInstanceKlass::get_non_static_field_by_offset(const int field_offset) { +ciField* ciInstanceKlass::get_nonstatic_field_by_offset(const int field_offset) { for (int i = 0, len = nof_nonstatic_fields(); i < len; i++) { ciField* field = _nonstatic_fields->at(i); int field_off = field->offset_in_bytes(); @@ -406,7 +406,7 @@ ciField* ciInstanceKlass::get_non_static_field_by_offset(const int field_offset) // ciInstanceKlass::get_field_by_offset ciField* ciInstanceKlass::get_field_by_offset(int field_offset, bool is_static) { if (!is_static) { - return get_non_static_field_by_offset(field_offset); + return get_nonstatic_field_by_offset(field_offset); } VM_ENTRY_MARK; InstanceKlass* k = get_instanceKlass(); @@ -437,7 +437,7 @@ ciField* ciInstanceKlass::get_field_by_name(ciSymbol* name, ciSymbol* signature, // except this does not require allocating memory for a new ciField BasicType ciInstanceKlass::get_field_type_by_offset(const int field_offset, const bool is_static) { if (!is_static) { - ciField* field = get_non_static_field_by_offset(field_offset); + ciField* field = get_nonstatic_field_by_offset(field_offset); return field != nullptr ? field->layout_type() : T_ILLEGAL; } diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp index 8ccf1fadfb7..a84c63981c9 100644 --- a/src/hotspot/share/ci/ciInstanceKlass.hpp +++ b/src/hotspot/share/ci/ciInstanceKlass.hpp @@ -83,7 +83,7 @@ private: bool compute_injected_fields_helper(); void compute_transitive_interfaces(); - ciField* get_non_static_field_by_offset(int field_offset); + ciField* get_nonstatic_field_by_offset(int field_offset); protected: ciInstanceKlass(Klass* k); From ef345e78797026b946aa5d91d5f6101ef3ad30bf Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Tue, 10 Feb 2026 14:23:23 +0000 Subject: [PATCH 197/215] 8377457: java/util/logging/ParentLoggersTest.java failed intermittently Reviewed-by: alanb, jpai --- .../share/classes/java/util/logging/Logger.java | 9 +++++---- test/jdk/java/util/logging/ParentLoggersTest.java | 11 ++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/java.logging/share/classes/java/util/logging/Logger.java b/src/java.logging/share/classes/java/util/logging/Logger.java index f40e419c2ca..dce0f69fa96 100644 --- a/src/java.logging/share/classes/java/util/logging/Logger.java +++ b/src/java.logging/share/classes/java/util/logging/Logger.java @@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2358,8 +2358,9 @@ public class Logger { synchronized (treeLock) { - // Remove ourself from any previous parent. + // Remove ourselves from any previous parent. LogManager.LoggerWeakRef ref = null; + Logger parent = this.parent; if (parent != null) { // assert parent.kids != null; for (Iterator iter = parent.kids.iterator(); iter.hasNext(); ) { @@ -2372,11 +2373,11 @@ public class Logger { ref = null; } } - // We have now removed ourself from our parents' kids. + // We have now removed ourselves from our parents' kids. } // Set our new parent. - parent = newParent; + this.parent = parent = newParent; if (parent.kids == null) { parent.kids = new ArrayList<>(2); } diff --git a/test/jdk/java/util/logging/ParentLoggersTest.java b/test/jdk/java/util/logging/ParentLoggersTest.java index 9150675c017..8aae7e95d74 100644 --- a/test/jdk/java/util/logging/ParentLoggersTest.java +++ b/test/jdk/java/util/logging/ParentLoggersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 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 @@ -23,7 +23,7 @@ /* * @test - * @bug 6498300 + * @bug 6498300 8377457 * * @summary regression: parent loggers are not properly registered * @author ss45998 @@ -77,6 +77,8 @@ public class ParentLoggersTest { initialLoggerNames.add(logger); } } + System.out.println("# default loggers are: " + defaultLoggers); + System.out.println("# initial loggers are: " + initialLoggerNames); String tstSrc = System.getProperty(TST_SRC_PROP); File fname = new File(tstSrc, LM_PROP_FNAME); @@ -131,7 +133,10 @@ public class ParentLoggersTest { } } - System.out.println(returnedLoggerNames); + System.out.println("# Created loggers are: " + + createdLoggers.stream().map(Logger::getName).toList()); + System.out.println("# Expected loggers are: " + expectedLoggerNames); + System.out.println("# Returned loggers are: " + returnedLoggerNames); return checkNames(expectedLoggerNames, returnedLoggerNames, failMsg); } From d97ea5a8cdd6cb033e7849515425880e9132b3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jeli=C5=84ski?= Date: Tue, 10 Feb 2026 14:48:52 +0000 Subject: [PATCH 198/215] 8377549: [BACKOUT] Need to keep leading zeros in TlsPremasterSecret of TLS1.3 DHKeyAgreement Reviewed-by: mullan --- .../share/classes/sun/security/ssl/KAKeyDerivation.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java index af62faf4706..39e82b50435 100644 --- a/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java +++ b/src/java.base/share/classes/sun/security/ssl/KAKeyDerivation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -214,13 +214,13 @@ public class KAKeyDerivation implements SSLKeyDerivation { var decapsulator = kem.newDecapsulator(localPrivateKey); sharedSecret = decapsulator.decapsulate( keyshare, 0, decapsulator.secretSize(), - "Generic"); + "TlsPremasterSecret"); } else { // Using traditional DH-style Key Agreement KeyAgreement ka = KeyAgreement.getInstance(algorithmName); ka.init(localPrivateKey); ka.doPhase(peerPublicKey, true); - sharedSecret = ka.generateSecret("Generic"); + sharedSecret = ka.generateSecret("TlsPremasterSecret"); } return deriveHandshakeSecret(type, sharedSecret); From 43b2d2bddf8d7c308c8d2782456cb3ab3a33993e Mon Sep 17 00:00:00 2001 From: Anton Artemov Date: Tue, 10 Feb 2026 16:41:42 +0000 Subject: [PATCH 199/215] 8375285: Port fdlibm asinh to Java Reviewed-by: darcy, rgiulietti --- .../share/classes/java/lang/FdLibm.java | 55 ++- .../share/classes/java/lang/Math.java | 31 +- .../share/classes/java/lang/StrictMath.java | 30 +- test/jdk/java/lang/Math/HyperbolicTests.java | 346 +++++++++++++++++- .../java/lang/StrictMath/ExhaustingTests.java | 4 +- .../java/lang/StrictMath/FdlibmTranslit.java | 46 ++- .../java/lang/StrictMath/HyperbolicTests.java | 87 ++++- 7 files changed, 586 insertions(+), 13 deletions(-) diff --git a/src/java.base/share/classes/java/lang/FdLibm.java b/src/java.base/share/classes/java/lang/FdLibm.java index 73e1da46af4..78090be2b05 100644 --- a/src/java.base/share/classes/java/lang/FdLibm.java +++ b/src/java.base/share/classes/java/lang/FdLibm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -3508,4 +3508,57 @@ final class FdLibm { return iz; } } + + /** + * Return the Inverse Hyperbolic Sine of x + * + * Method : + * + * + * asinh(x) is defined so that asinh(sinh(alpha)) = alpha, -∞ < alpha < ∞ + * and sinh(asinh(x)) = x, -∞ < x < ∞ + * It can be written as asinh(x) = sign(x) * ln(|x| + sqrt(x^2 + 1)), -∞ < x < ∞ + * 1. Replace x by |x| as the function is odd. + * 2. + * asinh(x) := x, if 1+x^2 = 1, + * := sign(x)*(log(x)+ln2)) for large |x|, else + * := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else + * := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2))) + * + * + * + * Special cases: + * only asinh(±0)=±0 is exact for finite x. + * asinh(NaN) is NaN + * asinh(±∞) is ±∞ + */ + static final class Asinh { + private static final double ln2 = 6.93147180559945286227e-01; + private static final double huge = 1.0e300; + + static double compute(double x) { + double t, w; + int hx, ix; + hx = __HI(x); + ix = hx & 0x7fff_ffff; + if (ix >= 0x7ff0_0000) { + return x + x; // x is inf or NaN + } + if (ix < 0x3e30_0000) { // |x| < 2**-28 + if (huge + x > 1.0) { + return x; // return x inexact except 0 + } + } + if (ix > 0x41b0_0000) { // |x| > 2**28 + w = Log.compute(Math.abs(x)) + ln2; + } else if (ix > 0x4000_0000) { // 2**28 > |x| > 2.0 + t = Math.abs(x); + w = Log.compute(2.0 * t + 1.0 / (Sqrt.compute(x * x + 1.0) + t)); + } else { // 2.0 > |x| > 2**-28 + t = x * x; + w = Log1p.compute(Math.abs(x) + t / (1.0 + Sqrt.compute(1.0 + t))); + } + return hx > 0 ? w : -w; + } + } } diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 55659bed57b..7add99f9325 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -108,8 +108,8 @@ import static java.lang.Double.*; * sin}, {@link cos cos}, {@link tan tan}, {@link asin asin}, {@link * acos acos}, {@link atan atan}, {@link exp exp}, {@link expm1 * expm1}, {@link log log}, {@link log10 log10}, {@link log1p log1p}, - * {@link sinh sinh}, {@link cosh cosh}, {@link tanh tanh}, {@link - * hypot hypot}, and {@link pow pow}. (The {@link sqrt sqrt} + * {@link sinh sinh}, {@link cosh cosh}, {@link tanh tanh}, {@link asinh asinh}, + * {@link hypot hypot}, and {@link pow pow}. (The {@link sqrt sqrt} * operation is a required part of IEEE 754 from a different section * of the standard.) The special case behavior of the recommended * operations generally follows the guidance of the IEEE 754 @@ -2758,6 +2758,33 @@ public final class Math { return StrictMath.tanh(x); } + /** + * Returns the inverse hyperbolic sine of a {@code double} value. + * The inverse hyperbolic sine of x is defined to be the function such that + * asinh({@linkplain Math#sinh sinh(x)}) = x for any x. + * Note that both domain and range of the exact asinh are unrestricted. + *

      Special cases: + *

        + * + *
      • If the argument is zero, then the result is a zero with the + * same sign as the argument. + * + *
      • If the argument is infinity, then the result is + * infinity with the same sign as the argument. + * + *
      • If the argument is NaN, then the result is NaN. + * + * + *
      + *

      The computed result must be within 2.5 ulps of the exact result. + * @param x The number whose inverse hyperbolic sine is to be returned. + * @return The inverse hyperbolic sine of {@code x}. + * @since 27 + */ + public static double asinh(double x) { + return StrictMath.asinh(x); + } + /** * Returns sqrt(x2 +y2) * without intermediate overflow or underflow. diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java index 499fce73aee..9540c4b05b4 100644 --- a/src/java.base/share/classes/java/lang/StrictMath.java +++ b/src/java.base/share/classes/java/lang/StrictMath.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 @@ -76,7 +76,8 @@ import jdk.internal.vm.annotation.IntrinsicCandidate; * {@code exp}, {@code log}, {@code log10}, * {@code cbrt}, {@code atan2}, {@code pow}, * {@code sinh}, {@code cosh}, {@code tanh}, - * {@code hypot}, {@code expm1}, and {@code log1p}. + * {@code asinh}, {@code hypot}, {@code expm1}, + * and {@code log1p}. * *

      * The platform uses signed two's complement integer arithmetic with @@ -2170,6 +2171,31 @@ public final class StrictMath { return FdLibm.Tanh.compute(x); } + /** + * Returns the inverse hyperbolic sine of a {@code double} value. + * The inverse hyperbolic sine of x is defined to be the function such that + * asinh({@linkplain Math#sinh sinh(x)}) = x for any x. + * Note that both domain and range of the exact asinh are unrestricted. + *

      Special cases: + *

        + * + *
      • If the argument is zero, then the result is a zero with the + * same sign as the argument. + * + *
      • If the argument is infinity, then the result is + * infinity with the same sign as the argument. + * + *
      • If the argument is NaN, then the result is NaN. + * + *
      + * @param x The number whose inverse hyperbolic sine is to be returned. + * @return The inverse hyperbolic sine of {@code x}. + * @since 27 + */ + public static double asinh(double x) { + return FdLibm.Asinh.compute(x); + } + /** * Returns sqrt(x2 +y2) * without intermediate overflow or underflow. diff --git a/test/jdk/java/lang/Math/HyperbolicTests.java b/test/jdk/java/lang/Math/HyperbolicTests.java index 4534396ee06..6f4fad94f6b 100644 --- a/test/jdk/java/lang/Math/HyperbolicTests.java +++ b/test/jdk/java/lang/Math/HyperbolicTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, 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 @@ -27,7 +27,7 @@ * @build Tests * @build HyperbolicTests * @run main HyperbolicTests - * @summary Tests for {Math, StrictMath}.{sinh, cosh, tanh} + * @summary Tests for {Math, StrictMath}.{sinh, cosh, tanh, asinh} */ import static java.lang.Double.longBitsToDouble; @@ -43,6 +43,7 @@ public class HyperbolicTests { failures += testSinh(); failures += testCosh(); failures += testTanh(); + failures += testAsinh(); if (failures > 0) { System.err.println("Testing the hyperbolic functions incurred " @@ -1390,4 +1391,345 @@ public class HyperbolicTests { failures += Tests.testUlpDiffWithAbsBound("StrictMath.tanh", -input, StrictMath::tanh, -expected, ulps, 1.0); return failures; } + + /** + * Test accuracy of {Math, StrictMath}.asinh. The specified + * accuracy is 2.5 ulps. + * + * The defintion of asinh(x) is + * + * asinh(sinh(x)) = x + * + * Can be also written as + * + * asinh(x) = ln(x + sqrt(x*x + 1)) + * + * The series expansion of asinh(x) = + * + * x - x^3/6 + 3x^5/40 - 15x^7/336 + ... + * + * Therefore, + * + * 1. For small values of x, asinh(x) ~= x. + * + * Additionally, asinh is an odd function; asinh(-x) = -asinh(x). + * + */ + static int testAsinh() { + int failures = 0; + /* + * Array elements below generated using a quad asinh + * implementation. Rounded to a double, the quad result + * *should* be correctly rounded, unless we are quite unlucky. + * Assuming the quad value is a correctly rounded double, the + * allowed error is 3.0 ulps instead of 2.5 since the quad + * value rounded to double can have its own 1/2 ulp error. + */ + double [][] testCases = { + // x asinh(x) + {+8.08576586004706676646947016706690192e+00 , +2.78705446940175084037519072068240843e+00 }, + {+5.71198448718283557923314219806343317e+00 , +2.44328951112537869179362382769224610e+00 }, + {+5.13969133196956207143557548988610506e+00 , +2.33947242588811531576718987896918712e+00 }, + {+2.95996078504353032911922127823345363e+00 , +1.80570832446450945675615676944433187e+00 }, + {+4.16200980791957864113328469102270901e+00 , +2.13327472868053919998765640756092875e+00 }, + {+3.69545071772785460595400763850193471e+00 , +2.01807302682646291458685219028816252e+00 }, + {+1.37724078256330018099617973348358646e+00 , +1.12468193910119796648577714130477538e+00 }, + {+8.42544369040329677034151245607063174e+00 , +2.82790657643842246518401899587920414e+00 }, + {+3.91229039061375782893037467147223651e+00 , +2.07321736531667549827684130235156669e+00 }, + {+5.73782778462038400846267904853448272e+00 , +2.44773637486997939825874893068408245e+00 }, + {+4.99855755102017340618658636230975389e+00 , +2.31215541451092776291779112133540281e+00 }, + {+8.10100320171622989562365546589717269e+00 , +2.78892295229049846951081695322303143e+00 }, + {+6.40711498894297992734436775208450854e+00 , +2.55659134739804960225496541857063844e+00 }, + {+4.25639664782793492037171745323576033e+00 , +2.15509221067754853744881157535735520e+00 }, + {+2.65061122655180847473843641637358814e+00 , +1.70175906719364776650786532519668576e+00 }, + {+3.14233404262031967846269253641366959e+00 , +1.86252059324271515969573278167449533e+00 }, + {+9.13083295141416328988270834088325500e+00 , +2.90778930976529177537743330080053959e+00 }, + {+9.34996922888940673601609887555241585e+00 , +2.93136773795688457445658002134152253e+00 }, + {+9.35132773574746245515143527882173657e+00 , +2.93151219897178112284587212807769745e+00 }, + {+1.47453686867873412502660812606336549e+00 , +1.18055530917929730380021873702412893e+00 }, + {+2.93708318858647077220780374773312360e+00 , +1.79836038745797816082436481350298220e+00 }, + {+4.09105467685985679793247982161119580e+00 , +2.11656320434131763040467250984368994e+00 }, + {+4.65121160775030162426446622703224421e+00 , +2.24163561547669988062740649953739512e+00 }, + {+8.83128600332669755346159945474937558e+00 , +2.87463799399744068863572725409458990e+00 }, + {+1.66240543263013162977870251779677346e+00 , +1.28160121300334421559587040333022048e+00 }, + {+4.58372145493984728403802364482544363e+00 , +2.22735027800562207352532152241704895e+00 }, + {+8.50271775332108248335316602606326342e+00 , +2.83697322459569158510957272430255446e+00 }, + {+8.82962635800362249938189052045345306e+00 , +2.87445124207064977822740131999732509e+00 }, + {+8.84552264926561093716372852213680744e+00 , +2.87623855339831018046947582541398439e+00 }, + {+3.12516324361785047258877057174686342e+00 , +1.85730059825198435603813107760370448e+00 }, + {+8.26480987017928114823916985187679529e+00 , +2.80879391905067271939218423695889677e+00 }, + {+9.68117403887023186825899756513535976e+00 , +2.96598712423961990106836581021055404e+00 }, + {+3.05372997660021550103692788979969919e+00 , +1.83530180662998049991392377010746750e+00 }, + {+4.40157829954937795946534606628119946e+00 , +2.18777148553475610871290743261274356e+00 }, + {+6.88055177977352627749496605247259140e+00 , +2.62708540897893209827881652490154282e+00 }, + {+5.98475681846005524988640900119207799e+00 , +2.48927078678959642161006322827411013e+00 }, + {+8.92994029487849694248779996996745467e+00 , +2.88567728733406656162446662917506943e+00 }, + {+3.12419716639021327608816136489622295e+00 , +1.85700613402604977963039277274730202e+00 }, + {+7.51161063055123179310612613335251808e+00 , +2.71399864345854847918135863803957037e+00 }, + {+1.17944590788822001314883891609497368e+00 , +1.00274792631001937429660854217557106e+00 }, + {+1.36421200272156672994583459512796253e+00 , +1.11700316010328101318941761315262905e+00 }, + {+6.88888479910741580169997178018093109e+00 , +2.62828320452398770553274423944254222e+00 }, + {+3.64699814082431839068476620013825595e+00 , +2.00533888971591775569462526134655508e+00 }, + {+1.41057156993997545590957543026888743e+00 , +1.14411132290221221244416446367286189e+00 }, + {+6.14945472638136703125155690941028297e+00 , +2.51605697935151660544323868421618014e+00 }, + {+6.02996213994895136067952989833429456e+00 , +2.49669366977555745163814756818745494e+00 }, + {+9.65271660518461160904735152143985033e+00 , +2.96305895992701909105343212587439308e+00 }, + {+3.48339981002946785793028539046645164e+00 , +1.96115002199047863383320140967526859e+00 }, + {+6.07995920803143352628694628947414458e+00 , +2.50484056235894688250578712446408489e+00 }, + {+6.00204567486939666309808671940118074e+00 , +2.49211610375224051369293822349155577e+00 }, + {+1.38833519546032624347731143643613905e+00 , +1.13118326188543957732445742827584178e+00 }, + {+4.52480967383013776839106867555528879e+00 , +2.21471559269073802833304398595496088e+00 }, + {+6.11810275538071746126433936296962202e+00 , +2.51101220932060589427300910397289711e+00 }, + {+7.50648056792778461954185331705957651e+00 , +2.71332143777867740825109248925661428e+00 }, + {+4.89658239129396388733539424720220268e+00 , +2.29195213807282224382341089459270833e+00 }, + {+2.79265817118974496446881516931171063e-02 , +2.79229530231546570990366385479747555e-02 }, + {+9.69315108025863381158160336781293154e+00 , +2.96721697176250274577477693915618144e+00 }, + {+7.83910637998812109827895255875773728e+00 , +2.75631566551255789285879320458504177e+00 }, + {+2.76576984498692324265789466153364629e+00 , +1.74165321687466923031477628544545184e+00 }, + {+4.99754435738235347486124737770296633e+00 , +2.31195663638515281361584735869808938e+00 }, + {+4.88922334987356776991873630322515965e+00 , +2.29047857490944287858591282873426921e+00 }, + {+2.78689754339832429508305722265504301e+00 , +1.74881289991707628903792141369254180e+00 }, + {+7.20931795462930935514123120810836554e+00 , +2.67329726418547732793921189411944602e+00 }, + {+8.33063517511005890980868571205064654e+00 , +2.81666990685356361031034871477442414e+00 }, + {+3.61188437237348836106320959515869617e+00 , +1.99601167970722876532263572785839092e+00 }, + {+5.32263649620131218398455530405044556e+00 , +2.37382580077770566473979021057511520e+00 }, + {+8.00366962656528002639788610395044088e+00 , +2.77692733912554343763823757722995583e+00 }, + {+4.59751681469834139193153532687574625e+00 , +2.23028654083102682106197643909523811e+00 }, + {+8.20253764279967612083055428229272366e+00 , +2.80128593985583624900402694440723735e+00 }, + {+8.81590969774593169461240904638543725e+00 , +2.87290644386803298326256037256286587e+00 }, + {+9.00323408655057377814046049024909735e+00 , +2.89380106762776953348709842638973283e+00 }, + {+7.21533058010746941590696224011480808e+00 , +2.67412302459536699184126235350224069e+00 }, + {+8.23695914498319758934030687669292092e+00 , +2.80544295419888555964661400747277886e+00 }, + {+9.66912482942344198022510681767016649e+00 , +2.96474834621717039972248353595994898e+00 }, + {+3.17602315814959101913927952409721911e+00 , +1.87268737666193382172385150977422494e+00 }, + {+6.90930101488594594627556944033131003e+00 , +2.63121186253032074408677485367709385e+00 }, + {+6.52386312488162189993090578354895115e+00 , +2.57443674633460769002260368508028529e+00 }, + {+8.33701897902340283152966549096163362e-01 , +7.58769250678348128717944053061372995e-01 }, + {+1.84073794668312284983358040335588157e+00 , +1.37005541332928719568137972151743439e+00 }, + {+3.74278586498477183752697783347684890e+00 , +2.03036413719335448282150464654423886e+00 }, + {+1.24005876679504356552286026271758601e+00 , +1.04136768247874493368196676622778561e+00 }, + {+5.46039591734140294931876269401982427e+00 , +2.39894972726732001224686289699275119e+00 }, + {+2.05576175637293001585703677847050130e+00 , +1.46829835554351749799178251325101620e+00 }, + {+8.14075272140118322283797169802710414e+00 , +2.79378100004325258561034629239868920e+00 }, + {+1.83236824312878154863426516385516152e+00 , +1.36605297794115641494849841805273118e+00 }, + {+3.75374287549383511830569659650791436e+00 , +2.03318857169224539730556063537088565e+00 }, + {+6.43073501869452779367009043198777363e-01 , +6.05407648939611618428355730173212664e-01 }, + {+3.48621711345758855671306264412123710e+00 , +1.96192711264291527976588288592889484e+00 }, + {+3.91549582965699016767757711932063103e+00 , +2.07401086477835132988304884524188567e+00 }, + {+8.71307615958979297943187702912837267e+00 , +2.86124897639861530832907097356321837e+00 }, + {+9.25125528837059185605085076531395316e+00 , +2.92081476128803069384228991876733421e+00 }, + {+1.39894257949993688905365729624463711e-01 , +1.39441932379308919610017101598940231e-01 }, + {+2.13018467023163493578863381117116660e+00 , +1.50038473636497909802939352641033551e+00 }, + {+9.11338663647286573166184098226949573e+00 , +2.90588816775704749530976417485641794e+00 }, + {+2.13672753800762960096903952944558114e+00 , +1.50316162138023144213989303271149548e+00 }, + {+1.84662954052762628975870029535144567e+00 , +1.37286438673796624203424974151164008e+00 }, + {+2.27538767726088808629469895095098764e+00 , +1.56042056675324794384613128836997008e+00 }, + {+2.25459809157452584216230206948239356e+00 , +1.55202382694053013875493092338973536e+00 }, + {+6.43951067826539436111943359719589353e+00 , +2.56157478187883218395455558699771338e+00 }, + {+2.38928979360439708301555583602748811e+00 , +1.60531075239512338397498990117642106e+00 } + }; + + + for (double [] testCase : testCases) { + failures += testAsinhCaseWithUlpDiff(testCase[0], + testCase[1], + 3.0); + } + + + + for (double nan : Tests.NaNs) { + failures += testAsinhCaseWithUlpDiff(nan, NaNd, 0); + } + + + + double [][] specialTestCases = { + {0.0, 0.0}, + {-0.0, -0.0}, + {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, + {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY} + }; + + + + for (double [] specialTestCase : specialTestCases) { + failures += testAsinhCaseWithUlpDiff(specialTestCase[0], + specialTestCase[1], + 0.0); + } + + + + // For powers of 2 less than 2^(-27), the second and + // subsequent terms of the Taylor series expansion will get + // rounded away since |n-n^3| > 53, the binary precision of a + // double significand. + + for (int i = DoubleConsts.MIN_SUB_EXPONENT; i < -27; i++) { + double d = Math.scalb(2.0, i); + + // Result and expected are the same. + failures += testAsinhCaseWithUlpDiff(d, d, 3.0); + } + + failures += testAsinhAdditionalTests(); + + return failures; + } + + /** + * Test accuracy of {Math, StrictMath}.asinh using quad precision + * asinh implementation as the reference. There are additional tests. + * The specified accuracy is 2.5 ulps. + * + */ + static int testAsinhAdditionalTests() { + int failures = 0; + /* + * Array elements below are generated using a quad precision asinh + * implementation (libquadmath). Rounded to a double, the quad result + * *should* be correctly rounded, unless we are quite unlucky. + * Assuming the quad value is a correctly rounded double, the + * allowed error is 3.0 ulps instead of 2.5 since the quad + * value rounded to double can have its own 1/2 ulp error. + */ + double[][] testCases = { + // x asinh(x) + {+9.29888520047217510822168407003118773e-03 , +9.29875119439016991620829879950089510e-03 }, + {+8.99606577522583765460506555200481671e-03 , +8.99594443891109052619951669093253709e-03 }, + {+1.14699795207729403345719987328266143e-03 , +1.14699770057820289010729363443748979e-03 }, + {-7.40424146583510897623447988280531717e-03 , -7.40417381397284073003520743847398090e-03 }, + {+5.49488608694304568602628791040842771e-03 , +5.49485843542812296005405619611903743e-03 }, + {-2.52116366329231764847884633695684897e-03 , -2.52116099243538701654807174519941378e-03 }, + {+8.06768422999586641564118139058336965e-03 , +8.06759671495411237747532966838824601e-03 }, + {+9.88178277836134454081840061689945287e-03 , +9.88162196002118907735979091611455166e-03 }, + {-9.74812907680771244256501262270830921e-03 , -9.74797469575697378811862965186905034e-03 }, + {+1.28957646989550853144912423431378556e-03 , +1.28957611246655855199674391222106077e-03 }, + {-3.58837408582282288427300898092653370e-03 , -3.58836638496033900344918006245058062e-03 }, + {+4.13062959563161372078532451723731356e-03 , +4.13061784951867272487598247250853139e-03 }, + {-2.51244549635140212301420703511212196e-03 , -2.51244285310613342444371438470610248e-03 }, + {+9.68636675928437156091188597883956390e-03 , +9.68621529398750501339156647788267837e-03 }, + {-5.46432162869262862125996349504930549e-03 , -5.46429443603366704988023092426655825e-03 }, + {-1.83386276898741575058782160567716346e-03 , -1.83386174109279550088776833959567103e-03 }, + {+4.29194713096967862819841599275605404e-03 , +4.29193395422167505495925421977757693e-03 }, + {-9.98345193989031744197237827620483586e-03 , -9.98328610669592860197569944928865818e-03 }, + {-2.77682511858849418590056146172173612e-03 , -2.77682155002991423153566829770018797e-03 }, + {-9.27080565792574341765774903478813940e-03 , -9.27067286211199059124030266083673465e-03 }, + {+2.13343832085455265001883162767626345e-03 , +2.13343670244611127381061946915639278e-03 }, + {+1.86531796573603282640707590189776965e-03 , +1.86531688403638317794548160776476123e-03 }, + {+7.59089952706850680519412577496041195e-03 , +7.59082662879913430594165031269929038e-03 }, + {-7.23221864571126256404642873576449347e-03 , -7.23215560034534649276205014022842148e-03 }, + {-4.85379569260787659124023463164121495e-03 , -4.85377663411205855718020189862812411e-03 }, + {+2.06427745583616026325834980070794700e-03 , +2.06427598977487382238201754110142048e-03 }, + {-9.05864273164906988466960058303811820e-03 , -9.05851884568408865775918913306811448e-03 }, + {+8.14126659074132334736884075709895114e-03 , +8.14117665926450847350600785486541236e-03 }, + {-1.18703607276676031956341716977476608e-03 , -1.18703579400048976745061906381442506e-03 }, + {-3.72556727069004237073990282169688726e-03 , -3.72555865235713390047416035972567655e-03 }, + {+3.20078632768614965153908258344017668e-03 , +3.20078086235102579132641956617880741e-03 }, + {-3.64652695868728470018904630478573381e-03 , -3.64651887732759812624780495337361570e-03 }, + {-4.37271109890237635181575726051050879e-03 , -4.37269716421058504020032749615645348e-03 }, + {-4.53954699201700607319454050525564526e-03 , -4.53953140071906668014572342681250395e-03 }, + {-7.71410922638161335174178390161614516e-03 , -7.71403272056205844933819193169521112e-03 }, + {-4.14748385016995048391041933655287721e-03 , -4.14747195968688563991362285984749351e-03 }, + {+2.16239643776180469336711809091866598e-03 , +2.16239475255273602474341490749538226e-03 }, + {+1.70867505939914857138184345330955693e-04 , +1.70867505108481996929933792720554316e-04 }, + {+4.45468038573873341412490134416657384e-03 , +4.45466565262570403928813346774794963e-03 }, + {+5.02848450175214997659445259614585666e-04 , +5.02848428983795534702560585886961806e-04 }, + {+3.27121347478520618778929929248988628e-03 , +3.27120764069260936386068328808026876e-03 }, + {-3.02035237923277844612757192521712568e-03 , -3.02034778704318937598236175985252813e-03 }, + {+1.83493544153568409471599665039320826e-03 , +1.83493441183628601773262459027151432e-03 }, + {-3.57742891707456961425393160425301176e-03 , -3.57742128646403200576149317708586166e-03 }, + {+4.07461518018882584701856330866576172e-04 , +4.07461506744091232910512108590665082e-04 }, + {+8.93213245095503781401102827430804609e-03 , +8.93201368317984717551252673740071605e-03 }, + {+4.83633433470810093768310622408534982e-03 , +4.83631548115857258701091393657125083e-03 }, + {+4.41941080408745669283465673515820527e-03 , +4.41939641815384450220145838057678415e-03 }, + {-7.40905574125878471636319488879962591e-03 , -7.40898795735011644277922058251845971e-03 }, + {+5.86417821400006013254913028731607483e-03 , +5.86414460438723375893640532030031887e-03 }, + {+6.40143962390532852979596611930901418e-04 , +6.40143918670384118303282950462728667e-04 }, + {-6.62414482978957927516994175221043406e-03 , -6.62409638694556116425732364897473820e-03 }, + {+2.32034013904017406837443360245742952e-03 , +2.32033805693503550464566970496041304e-03 }, + {-1.70037238711657177903102677873903303e-03 , -1.70037156774608724006177273975196346e-03 }, + {-2.41007559448008345376335270771051000e-03 , -2.41007326134647811152942503146226815e-03 }, + {-4.88371906557288470301925187300184916e-03 , -4.88369965241849364892773140816442366e-03 }, + {+5.45353417327115556900718473798406194e-03 , +5.45350714134116714399404074418610335e-03 }, + {+1.82127472834406854695910027430727496e-03 , +1.82127372147155402291508536733347481e-03 }, + {+3.08464771244317328968698177504847990e-03 , +3.08464282070047297125571891308421168e-03 }, + {+8.64099586698139320029010690404902562e-03 , +8.64088833799551442516038310825829002e-03 }, + {+5.55586600363066999974659410099775414e-03 , +5.55583742127587993316327594170344732e-03 }, + {+8.72688321697448303460031837630594964e-03 , +8.72677244972821936872995155020997092e-03 }, + {+8.67310115846320860144569309113649069e-03 , +8.67299242648623772707657889109267822e-03 }, + {-6.14210531791088872044515056813906995e-03 , -6.14206669961072319996743834488437463e-03 }, + {+8.88625749891074935560286718327915878e-03 , +8.88614055166480866111859091126682642e-03 }, + {-1.43504119833010382323301712403917918e-03 , -1.43504070579016156191011766070535043e-03 }, + {+8.88630021979499445938799340183322784e-04 , +8.88629905026452701546356648558325925e-04 }, + {+3.93780142608554303840229238176107174e-03 , +3.93779124938125782164719425287579139e-03 }, + {+5.97434710953371854447180311353804427e-03 , +5.97431156988517485964304752157241804e-03 }, + {-7.98875566707221360096546902695990866e-03 , -7.98867069549231529699110336543851522e-03 }, + {-7.35260998042218307663153709086145682e-03 , -7.35254373394740372183361138755444401e-03 }, + {-1.21452151194481779150624589647122775e-03 , -1.21452121336249242914680975845323467e-03 }, + {+4.98481949753720385287714123023761204e-04 , +4.98481929109570105039386663388380124e-04 }, + {+4.91985816350236880578616904813316069e-03 , +4.91983831618717601288972307767102048e-03 }, + {+7.68796449908712258014542584305672790e-03 , +7.68788876850302034489862036714171394e-03 }, + {-1.49188964399755812084702455422302592e-03 , -1.49188909057301843050666760106824645e-03 }, + {-6.73163270527497858974896871586679481e-03 , -6.73158186579155909561057079482268382e-03 }, + {+8.73326787816911150053034162965559517e-03 , +8.73315686763575988088834491175495305e-03 }, + {+7.81885100909627574206073319373899722e-03 , +7.81877134445315065535398733750043537e-03 }, + {+5.17883696377165365920536288513176260e-03 , +5.17881381434573686588090532152532759e-03 }, + {+5.94443020139156542980263253639350296e-03 , +5.94439519296920978949883610708856872e-03 }, + {+6.20408692607689153664107806207539397e-03 , +6.20404712683039987243003869674263039e-03 }, + {-4.13924636982772493898341537033047643e-03 , -4.13923455005213957426577773895357676e-03 }, + {-3.71140287747976152510354097557865316e-03 , -3.71139435707241790669973810798603847e-03 }, + {-8.37941685260259506995428324671593145e-03 , -8.37931879609631048090526043629803007e-03 }, + {+8.48517293301665094518160259440264781e-03 , +8.48507111684271940482025173385456325e-03 }, + {+8.52827781082153742187035305732933921e-03 , +8.52817443510010657036791986083451790e-03 }, + {-4.07416024108964158756407414330169559e-03 , -4.07414897015777394146227881635448891e-03 }, + {+4.33240979016216026797891913702187594e-03 , +4.33239623721743479728849041379241354e-03 }, + {-4.89052581083683143847729013486969052e-04 , -4.89052561589036445343259711601066150e-04 }, + {-9.62974650385375885441874288517283276e-05 , -9.62974648897066185792218187524452666e-05 }, + {-9.26529020589744040092838872624270152e-04 , -9.26528888025725086174151901318195528e-04 }, + {+1.39965317129459553002757132844635635e-03 , +1.39965271430147299997462291249440004e-03 }, + {-7.12079193844885119379917171045235591e-03 , -7.12073176239158986696262840488189056e-03 }, + {-3.62621830991397085114380516301935131e-03 , -3.62621036282602413990843595245483708e-03 }, + {-7.11170226471406416446363607519742800e-03 , -7.11164231880305326913177834599584014e-03 }, + {-1.07788116725225993630665755063091638e-04 , -1.07788116516507275261580974984359313e-04 }, + {-2.51343271020868386927960003163207148e-03 , -2.51343006384636979438035563375611134e-03 }, + {-9.70989675056706420808172453007500735e-03 , -9.70974417880563379443605277365308469e-03 }, + {+7.17488567146307328059595675995296915e-03 , +7.17482411358449384368065167506580475e-03 } + }; + + for (int i = 0; i < testCases.length; i++) { + double[] testCase = testCases[i]; + failures += testAsinhCaseWithUlpDiff(testCase[0], + testCase[1], + 3.0); + } + + return failures; + } + + public static int testAsinhCaseWithTolerance(double input, + double expected, + double tolerance) { + int failures = 0; + failures += Tests.testTolerance("Math.asinh", input, Math::asinh, expected, tolerance); + failures += Tests.testTolerance("Math.asinh", -input, Math::asinh, -expected, tolerance); + + failures += Tests.testTolerance("StrictMath.asinh", input, StrictMath::asinh, expected, tolerance); + failures += Tests.testTolerance("StrictMath.asinh", -input, StrictMath::asinh, -expected, tolerance); + return failures; + } + + public static int testAsinhCaseWithUlpDiff(double input, + double expected, + double ulps) { + int failures = 0; + + failures += Tests.testUlpDiffWithAbsBound("Math.asinh", input, Math::asinh, expected, ulps, Double.POSITIVE_INFINITY); + failures += Tests.testUlpDiffWithAbsBound("Math.asinh", -input, Math::asinh, -expected, ulps, Double.NEGATIVE_INFINITY); + + failures += Tests.testUlpDiffWithAbsBound("StrictMath.asinh", input, StrictMath::asinh, expected, ulps, Double.POSITIVE_INFINITY); + failures += Tests.testUlpDiffWithAbsBound("StrictMath.asinh", -input, StrictMath::asinh, -expected, ulps, Double.NEGATIVE_INFINITY); + return failures; + } } diff --git a/test/jdk/java/lang/StrictMath/ExhaustingTests.java b/test/jdk/java/lang/StrictMath/ExhaustingTests.java index 0351caff70c..143227c4cc3 100644 --- a/test/jdk/java/lang/StrictMath/ExhaustingTests.java +++ b/test/jdk/java/lang/StrictMath/ExhaustingTests.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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -91,6 +91,8 @@ public class ExhaustingTests { new UnaryTestCase("asin", FdlibmTranslit::asin, StrictMath::asin, DEFAULT_SHIFT), new UnaryTestCase("acos", FdlibmTranslit::acos, StrictMath::acos, DEFAULT_SHIFT), new UnaryTestCase("atan", FdlibmTranslit::atan, StrictMath::atan, DEFAULT_SHIFT), + + new UnaryTestCase("asinh", FdlibmTranslit::asinh, StrictMath::asinh, DEFAULT_SHIFT), }; for (var testCase : testCases) { diff --git a/test/jdk/java/lang/StrictMath/FdlibmTranslit.java b/test/jdk/java/lang/StrictMath/FdlibmTranslit.java index f38ca68569b..3001fed911f 100644 --- a/test/jdk/java/lang/StrictMath/FdlibmTranslit.java +++ b/test/jdk/java/lang/StrictMath/FdlibmTranslit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -140,6 +140,10 @@ public class FdlibmTranslit { return Tanh.compute(x); } + public static double asinh(double x) { + return Asinh.compute(x); + } + public static double IEEEremainder(double f1, double f2) { return IEEEremainder.compute(f1, f2); } @@ -2752,4 +2756,44 @@ public class FdlibmTranslit { return x; /* exact output */ } } + + /* + * Return the Inverse Hyperbolic Sine of x + * + * Method : + * + * Based on + * asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ] + * we have + * asinh(x) := x if 1+x*x=1, + * := sign(x)*(log(x)+ln2)) for large |x|, else + * := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else + * := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2))) + */ + private static final class Asinh { + private static final double one = 1.0; + private static final double ln2 = 6.93147180559945286227e-01; + private static final double huge = 1.0e300; + + static double compute(double x) { + double t,w; + int hx,ix; + hx = __HI(x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return x+x; /* x is inf or NaN */ + if(ix< 0x3e300000) { /* |x|<2**-28 */ + if(huge+x>one) return x; /* return x inexact except 0 */ + } + if(ix>0x41b00000) { /* |x| > 2**28 */ + w = log(Math.abs(x))+ln2; + } else if (ix>0x40000000) { /* 2**28 > |x| > 2.0 */ + t = Math.abs(x); + w = log(2.0*t+one/(sqrt(x*x+one)+t)); + } else { /* 2.0 > |x| > 2**-28 */ + t = x*x; + w =log1p(Math.abs(x)+t/(one+sqrt(one+t))); + } + if(hx>0) return w; else return -w; + } + } } diff --git a/test/jdk/java/lang/StrictMath/HyperbolicTests.java b/test/jdk/java/lang/StrictMath/HyperbolicTests.java index 1f570ce9efd..6dbf3cd90ee 100644 --- a/test/jdk/java/lang/StrictMath/HyperbolicTests.java +++ b/test/jdk/java/lang/StrictMath/HyperbolicTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -26,7 +26,7 @@ import java.util.function.DoubleUnaryOperator; /* * @test - * @bug 4851625 8301444 + * @bug 4851625 8301444 8331354 * @key randomness * @library /test/lib * @build jdk.test.lib.RandomFactory @@ -34,7 +34,7 @@ import java.util.function.DoubleUnaryOperator; * @build FdlibmTranslit * @build HyperbolicTests * @run main HyperbolicTests - * @summary Tests for StrictMath.{sinh, cosh, tanh} + * @summary Tests for StrictMath.{sinh, cosh, tanh, asinh} */ /** @@ -64,6 +64,7 @@ public class HyperbolicTests { failures += testSinh(); failures += testCosh(); failures += testTanh(); + failures += testAsinh(); if (failures > 0) { System.err.println("Testing the hyperbolics incurred " @@ -78,7 +79,8 @@ public class HyperbolicTests { private static enum HyperbolicTest { SINH(HyperbolicTests::testSinhCase, FdlibmTranslit::sinh), COSH(HyperbolicTests::testCoshCase, FdlibmTranslit::cosh), - TANH(HyperbolicTests::testTanhCase, FdlibmTranslit::tanh); + TANH(HyperbolicTests::testTanhCase, FdlibmTranslit::tanh), + ASINH(HyperbolicTests::testAsinhCase, FdlibmTranslit::asinh); private DoubleDoubleToInt testCase; private DoubleUnaryOperator transliteration; @@ -253,6 +255,11 @@ public class HyperbolicTests { StrictMath::tanh, expected); } + private static int testAsinhCase(double input, double expected) { + return Tests.test("StrictMath.asinh(double)", input, + StrictMath::asinh, expected); + } + private static int testSinh() { int failures = 0; double [][] testCases = { @@ -484,4 +491,76 @@ public class HyperbolicTests { return failures; } + + private static int testAsinh() { + int failures = 0; + double [][] testCases = { + {0x1.5798ee2308c36p-27, 0x1.5798ee2308c35p-27}, + {0x1.ffffffffffffep-26, 0x1.ffffffffffffdp-26}, + {0x1.ffffffffffffep-25, 0x1.ffffffffffff9p-25}, + {0x1.ad7f29abcaf47p-24, 0x1.ad7f29abcaf3bp-24}, + {0x1.ad7f29abcaf48p-24, 0x1.ad7f29abcaf3cp-24}, + {0x1.ffffffffffffep-24, 0x1.fffffffffffe9p-24}, + {0x1.ffffffffffffep-23, 0x1.fffffffffffa9p-23}, + {0x1.ffffffffffffep-22, 0x1.ffffffffffea9p-22}, + {0x1.ffffffffffffep-21, 0x1.ffffffffffaa9p-21}, + {0x1.0c6f7a0b5ed8dp-20, 0x1.0c6f7a0b5ea7ap-20}, + {0x1.ffffffffffffep-20, 0x1.fffffffffeaa9p-20}, + {0x1.ffffffffffffep-19, 0x1.fffffffffaaa9p-19}, + {0x1.fffffffffffffp-18, 0x1.ffffffffeaaa9p-18}, + {0x1p-17, 0x1.ffffffffeaaabp-18}, + {0x1.4f8b588e368edp-17, 0x1.4f8b588e1e89ep-17}, + {0x1.fffffffffffffp-17, 0x1.ffffffffaaaa9p-17}, + {0x1.fffffffffffffp-16, 0x1.fffffffeaaaa9p-16}, + {0x1p-15, 0x1.fffffffeaaaabp-16}, + {0x1.fffffffffe5ddp-15, 0x1.fffffffaa9087p-15}, + {0x1.fffffffffffffp-15, 0x1.fffffffaaaaa9p-15}, + {0x1.a36e2eb1c432dp-14, 0x1.a36e2ea609cc8p-14}, + {0x1.ffffffffffffep-14, 0x1.ffffffeaaaaa9p-14}, + {0x1p-13, 0x1.ffffffeaaaaabp-14}, + {0x1.ffffffffffd51p-13, 0x1.ffffffaaaa7fdp-13}, + {0x1.fffffffffffffp-13, 0x1.ffffffaaaaaadp-13}, + {0x1.ffffffffffffep-12, 0x1.fffffeaaaaacfp-12}, + {0x1p-11, 0x1.fffffeaaaaad1p-12}, + {0x1.fffffffffff1p-11, 0x1.fffffaaaaac21p-11}, + {0x1p-10, 0x1.fffffaaaaad11p-11}, + {0x1.0624dd2f1a9c6p-10, 0x1.0624da5218b5fp-10}, + {0x1.0624dd2f1a9f8p-10, 0x1.0624da5218b91p-10}, + {0x1.fffffffffffddp-10, 0x1.ffffeaaaad0edp-10}, + {0x1.fffffffffffffp-10, 0x1.ffffeaaaad10fp-10}, + {0x1.ffffffffffffcp-9, 0x1.ffffaaaad110cp-9}, + {0x1.ffffffffffffep-9, 0x1.ffffaaaad110ep-9}, + {0x1.ffffffffffff8p-8, 0x1.fffeaaad110aep-8}, + {0x1.ffffffffffffep-8, 0x1.fffeaaad110b4p-8}, + {0x1.47ae147ae1458p-7, 0x1.47acae9508ae4p-7}, + {0x1.47ae147ae1464p-7, 0x1.47acae9508afp-7}, + {0x1.ffffffffffffep-7, 0x1.fffaaad10fa35p-7}, + {0x1.fffffffffffffp-7, 0x1.fffaaad10fa35p-7}, + {0x1.ffffffffffff9p-6, 0x1.ffeaad10b5b28p-6}, + {0x1.ffffffffffffep-6, 0x1.ffeaad10b5b2bp-6}, + {0x1.ffffffffffff9p-5, 0x1.ffaad0fa4525bp-5}, + {0x1.fffffffffffffp-5, 0x1.ffaad0fa45261p-5}, + {0x1.9999999999996p-4, 0x1.98eb9e7e5fc3ap-4}, + {0x1.9999999999997p-4, 0x1.98eb9e7e5fc3bp-4}, + {0x1.fffffffffffffp-4, 0x1.fead0b6996972p-4}, + {0x1p-3, 0x1.fead0b6996972p-4}, + {0x1.fffffffffffffp-3, 0x1.facfb2399e636p-3}, + {0x1.ffffffffffffcp-2, 0x1.ecc2caec51605p-2}, + {0x1.ffffffffffffep-2, 0x1.ecc2caec51608p-2}, + {0x1.ffffffffffffbp-1, 0x1.c34366179d423p-1}, + {0x1.ffffffffffffep-1, 0x1.c34366179d426p-1}, + {0x1.fffffffffffd3p+0, 0x1.719218313d073p+0}, + {0x1.fffffffffffe1p+0, 0x1.719218313d079p+0}, + {0x1.ffffffffffed8p+1, 0x1.0c1f8a6e80ea4p+1}, + {0x1.fffffffffff92p+1, 0x1.0c1f8a6e80edp+1}, + {0x1.0108b83c4bbc8p-1, 0x1.ee9c256f3947ep-2}, + {-0x1.c41e527b70f43p-3, -0x1.c0863c7dece22p-3}, + }; + + for (double[] testCase: testCases) { + failures += testAsinhCase(testCase[0], testCase[1]); + } + + return failures; + } } From 21d4c6c68fc1199275b3317cd64ae24c8aeca003 Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Tue, 10 Feb 2026 16:50:17 +0000 Subject: [PATCH 200/215] 8377013: TimeZone.getDefault() returns obsolete id on Windows (Asia/Calcutta) Reviewed-by: jlu --- .../tools/cldrconverter/CLDRConverter.java | 12 ++++++++-- .../cldrconverter/TimeZoneParseHandler.java | 24 +++++++++++++++++-- .../cldrconverter/WinZonesParseHandler.java | 3 ++- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java index 55dd6a8d6ad..ab878a4d2a5 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/CLDRConverter.java @@ -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 @@ -293,8 +293,10 @@ public class CLDRConverter { bundleGenerator = new ResourceBundleGenerator(); // Parse data independent of locales - parseSupplemental(); + // parseBCP47() must precede parseSupplemental(). The latter depends + // on IANA alias map, which is produced by the former. parseBCP47(); + parseSupplemental(); // rules maps pluralRules = generateRules(handlerPlurals); @@ -536,6 +538,12 @@ public class CLDRConverter { // canonical tz name map // alias -> primary + // + // Note that CLDR meta zones do not necessarily align with IANA's + // current time zone identifiers. For example, the CLDR "India" + // meta zone maps to "Asia/Calcutta", whereas IANA now uses + // "Asia/Kolkata" for the zone. Accordingly, "canonical" here is + // defined in terms of CLDR's zone mappings. handlerTimeZone.getData().forEach((k, v) -> { String[] ids = ((String)v).split("\\s"); for (int i = 1; i < ids.length; i++) { diff --git a/make/jdk/src/classes/build/tools/cldrconverter/TimeZoneParseHandler.java b/make/jdk/src/classes/build/tools/cldrconverter/TimeZoneParseHandler.java index 66e94e5ca06..8203a9f0b91 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/TimeZoneParseHandler.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/TimeZoneParseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, 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 @@ -27,6 +27,9 @@ package build.tools.cldrconverter; import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import org.xml.sax.Attributes; import org.xml.sax.InputSource; @@ -40,6 +43,10 @@ import org.xml.sax.SAXException; class TimeZoneParseHandler extends AbstractLDMLHandler { private static final String PREF_PREFIX = "preferred:"; + // CLDR aliases to IANA ids map. The initial capacity is estimated + // from the number of aliases in timezone.xml as of CLDR v48 + private final Map ianaAliasMap = HashMap.newHashMap(32); + @Override public InputSource resolveEntity(String publicID, String systemID) throws IOException, SAXException { // avoid HTTP traffic to unicode.org @@ -61,7 +68,16 @@ class TimeZoneParseHandler extends AbstractLDMLHandler { put(attributes.getValue("name"), PREF_PREFIX + preferred); } } else { - put(attributes.getValue("name"), attributes.getValue("alias")); + var alias = attributes.getValue("alias"); + var iana = attributes.getValue("iana"); + if (iana != null) { + for (var a : alias.split("\\s+")) { + if (!a.equals(iana)) { + ianaAliasMap.put(a, iana); + } + } + } + put(attributes.getValue("name"), alias); } } break; @@ -80,4 +96,8 @@ class TimeZoneParseHandler extends AbstractLDMLHandler { .forEach(e -> map.put(e.getKey(), map.get(e.getValue().toString().substring(PREF_PREFIX.length())))); } + + Map getIanaAliasMap() { + return ianaAliasMap; + } } diff --git a/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java b/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java index a584358f0cb..343e143b6ad 100644 --- a/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java +++ b/make/jdk/src/classes/build/tools/cldrconverter/WinZonesParseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 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 @@ -56,6 +56,7 @@ class WinZonesParseHandler extends AbstractLDMLHandler { String zoneName = attributes.getValue("other"); String territory = attributes.getValue("territory"); String javatz = attributes.getValue("type").replaceFirst("\\s.*", ""); + javatz = CLDRConverter.handlerTimeZone.getIanaAliasMap().getOrDefault(javatz, javatz); put(zoneName + ":" + territory, javatz); pushIgnoredContainer(qName); break; From 7bc2475962efb690c11a8bfcaa25ab184475fb13 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 10 Feb 2026 17:07:56 +0000 Subject: [PATCH 201/215] 8377096: Refactor AOTMapLogger::OopDataIterator implementations Reviewed-by: eosterlund, kvn --- src/hotspot/share/cds/aotMapLogger.cpp | 6 +- src/hotspot/share/cds/aotMapLogger.hpp | 10 +- src/hotspot/share/cds/aotMappedHeap.cpp | 49 +++++ src/hotspot/share/cds/aotMappedHeap.hpp | 168 ++++++++++++++++++ src/hotspot/share/cds/aotMappedHeapLoader.cpp | 102 +++-------- src/hotspot/share/cds/aotMappedHeapLoader.hpp | 14 +- src/hotspot/share/cds/aotMappedHeapWriter.cpp | 100 ++--------- src/hotspot/share/cds/aotMappedHeapWriter.hpp | 12 +- src/hotspot/share/cds/aotMetaspace.cpp | 12 +- src/hotspot/share/cds/aotMetaspace.hpp | 10 +- src/hotspot/share/cds/aotStreamedHeap.cpp | 55 ++++++ src/hotspot/share/cds/aotStreamedHeap.hpp | 147 +++++++++++++++ .../share/cds/aotStreamedHeapLoader.cpp | 49 +---- .../share/cds/aotStreamedHeapWriter.cpp | 57 +----- .../share/cds/aotStreamedHeapWriter.hpp | 10 +- src/hotspot/share/cds/archiveBuilder.cpp | 6 +- src/hotspot/share/cds/archiveBuilder.hpp | 12 +- src/hotspot/share/cds/filemap.cpp | 10 +- src/hotspot/share/cds/filemap.hpp | 28 +-- src/hotspot/share/cds/heapShared.cpp | 51 +----- src/hotspot/share/cds/heapShared.hpp | 125 +------------ 21 files changed, 539 insertions(+), 494 deletions(-) create mode 100644 src/hotspot/share/cds/aotMappedHeap.cpp create mode 100644 src/hotspot/share/cds/aotMappedHeap.hpp create mode 100644 src/hotspot/share/cds/aotStreamedHeap.cpp create mode 100644 src/hotspot/share/cds/aotStreamedHeap.hpp diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index 5e4e0956824..fa769aee1bf 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -88,7 +88,7 @@ void AOTMapLogger::ergo_initialize() { } void AOTMapLogger::dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo, - ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info, + AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info, char* bitmap, size_t bitmap_size_in_bytes) { _is_runtime_logging = false; _buffer_to_requested_delta = ArchiveBuilder::current()->buffer_to_requested_delta(); @@ -823,7 +823,7 @@ public: } }; // AOTMapLogger::ArchivedFieldPrinter -void AOTMapLogger::dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* heap_info) { +void AOTMapLogger::dumptime_log_mapped_heap_region(AOTMappedHeapInfo* heap_info) { MemRegion r = heap_info->buffer_region(); address buffer_start = address(r.start()); // start of the current oop inside the buffer address buffer_end = address(r.end()); @@ -835,7 +835,7 @@ void AOTMapLogger::dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* heap_i log_archived_objects(AOTMappedHeapWriter::oop_iterator(heap_info)); } -void AOTMapLogger::dumptime_log_streamed_heap_region(ArchiveStreamedHeapInfo* heap_info) { +void AOTMapLogger::dumptime_log_streamed_heap_region(AOTStreamedHeapInfo* heap_info) { MemRegion r = heap_info->buffer_region(); address buffer_start = address(r.start()); // start of the current oop inside the buffer address buffer_end = address(r.end()); diff --git a/src/hotspot/share/cds/aotMapLogger.hpp b/src/hotspot/share/cds/aotMapLogger.hpp index bf7ce0028b9..f495ed97f40 100644 --- a/src/hotspot/share/cds/aotMapLogger.hpp +++ b/src/hotspot/share/cds/aotMapLogger.hpp @@ -33,8 +33,8 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" -class ArchiveMappedHeapInfo; -class ArchiveStreamedHeapInfo; +class AOTMappedHeapInfo; +class AOTStreamedHeapInfo; class CompileTrainingData; class DumpRegion; class FileMapInfo; @@ -157,8 +157,8 @@ private: #if INCLUDE_CDS_JAVA_HEAP - static void dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* mapped_heap_info); - static void dumptime_log_streamed_heap_region(ArchiveStreamedHeapInfo* streamed_heap_info); + static void dumptime_log_mapped_heap_region(AOTMappedHeapInfo* mapped_heap_info); + static void dumptime_log_streamed_heap_region(AOTStreamedHeapInfo* streamed_heap_info); static void runtime_log_heap_region(FileMapInfo* mapinfo); static void print_oop_info_cr(outputStream* st, FakeOop fake_oop, bool print_location = true); @@ -173,7 +173,7 @@ public: static bool is_logging_at_bootstrap() { return _is_logging_at_bootstrap; } static void dumptime_log(ArchiveBuilder* builder, FileMapInfo* mapinfo, - ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info, + AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info, char* bitmap, size_t bitmap_size_in_bytes); static void runtime_log(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo); }; diff --git a/src/hotspot/share/cds/aotMappedHeap.cpp b/src/hotspot/share/cds/aotMappedHeap.cpp new file mode 100644 index 00000000000..ba24c43eea1 --- /dev/null +++ b/src/hotspot/share/cds/aotMappedHeap.cpp @@ -0,0 +1,49 @@ +/* + * 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 "cds/aotMappedHeap.hpp" + +// Anything that goes in the header must be thoroughly purged from uninitialized memory +// as it will be written to disk. Therefore, the constructors memset the memory to 0. +// This is not the prettiest thing, but we need to know every byte is initialized, +// including potential padding between fields. + +AOTMappedHeapHeader::AOTMappedHeapHeader(size_t ptrmap_start_pos, + size_t oopmap_start_pos, + HeapRootSegments root_segments) { + memset((char*)this, 0, sizeof(*this)); + _ptrmap_start_pos = ptrmap_start_pos; + _oopmap_start_pos = oopmap_start_pos; + _root_segments = root_segments; +} + +AOTMappedHeapHeader::AOTMappedHeapHeader() { + memset((char*)this, 0, sizeof(*this)); +} + +AOTMappedHeapHeader AOTMappedHeapInfo::create_header() { + return AOTMappedHeapHeader{_ptrmap_start_pos, + _oopmap_start_pos, + _root_segments}; +} diff --git a/src/hotspot/share/cds/aotMappedHeap.hpp b/src/hotspot/share/cds/aotMappedHeap.hpp new file mode 100644 index 00000000000..307451b24d4 --- /dev/null +++ b/src/hotspot/share/cds/aotMappedHeap.hpp @@ -0,0 +1,168 @@ +/* + * 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. + * + */ + +#ifndef SHARE_CDS_AOTMAPPEDHEAP_HPP +#define SHARE_CDS_AOTMAPPEDHEAP_HPP + +#include "cds/aotMapLogger.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/macros.hpp" + +class AOTMappedHeapHeader { + size_t _ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the heap. + size_t _oopmap_start_pos; // The first bit in the oopmap corresponds to this position in the heap. + HeapRootSegments _root_segments; // Heap root segments info + +public: + AOTMappedHeapHeader(); + AOTMappedHeapHeader(size_t ptrmap_start_pos, + size_t oopmap_start_pos, + HeapRootSegments root_segments); + + size_t ptrmap_start_pos() const { return _ptrmap_start_pos; } + size_t oopmap_start_pos() const { return _oopmap_start_pos; } + HeapRootSegments root_segments() const { return _root_segments; } + + // This class is trivially copyable and assignable. + AOTMappedHeapHeader(const AOTMappedHeapHeader&) = default; + AOTMappedHeapHeader& operator=(const AOTMappedHeapHeader&) = default; +}; + +class AOTMappedHeapInfo { + MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive. + CHeapBitMap _oopmap; + CHeapBitMap _ptrmap; + HeapRootSegments _root_segments; + size_t _oopmap_start_pos; // How many zeros were removed from the beginning of the bit map? + size_t _ptrmap_start_pos; // How many zeros were removed from the beginning of the bit map? + +public: + AOTMappedHeapInfo() : + _buffer_region(), + _oopmap(128, mtClassShared), + _ptrmap(128, mtClassShared), + _root_segments(), + _oopmap_start_pos(), + _ptrmap_start_pos() {} + bool is_used() { return !_buffer_region.is_empty(); } + + MemRegion buffer_region() { return _buffer_region; } + void set_buffer_region(MemRegion r) { _buffer_region = r; } + + char* buffer_start() { return (char*)_buffer_region.start(); } + size_t buffer_byte_size() { return _buffer_region.byte_size(); } + + CHeapBitMap* oopmap() { return &_oopmap; } + CHeapBitMap* ptrmap() { return &_ptrmap; } + + void set_oopmap_start_pos(size_t start_pos) { _oopmap_start_pos = start_pos; } + void set_ptrmap_start_pos(size_t start_pos) { _ptrmap_start_pos = start_pos; } + + void set_root_segments(HeapRootSegments segments) { _root_segments = segments; }; + HeapRootSegments root_segments() { return _root_segments; } + + AOTMappedHeapHeader create_header(); +}; + +#if INCLUDE_CDS_JAVA_HEAP +class AOTMappedHeapOopIterator : public AOTMapLogger::OopDataIterator { +protected: + address _current; + address _next; + + address _buffer_start; + address _buffer_end; + uint64_t _buffer_start_narrow_oop; + intptr_t _buffer_to_requested_delta; + int _requested_shift; + + size_t _num_root_segments; + size_t _num_obj_arrays_logged; + +public: + AOTMappedHeapOopIterator(address buffer_start, + address buffer_end, + address requested_base, + address requested_start, + int requested_shift, + size_t num_root_segments) + : _current(nullptr), + _next(buffer_start), + _buffer_start(buffer_start), + _buffer_end(buffer_end), + _requested_shift(requested_shift), + _num_root_segments(num_root_segments), + _num_obj_arrays_logged(0) { + _buffer_to_requested_delta = requested_start - buffer_start; + _buffer_start_narrow_oop = 0xdeadbeed; + if (UseCompressedOops) { + _buffer_start_narrow_oop = (uint64_t)(pointer_delta(requested_start, requested_base, 1)) >> requested_shift; + assert(_buffer_start_narrow_oop < 0xffffffff, "sanity"); + } + } + + virtual AOTMapLogger::OopData capture(address buffered_addr) = 0; + + bool has_next() override { + return _next < _buffer_end; + } + + AOTMapLogger::OopData next() override { + _current = _next; + AOTMapLogger::OopData result = capture(_current); + if (result._klass->is_objArray_klass()) { + result._is_root_segment = _num_obj_arrays_logged++ < _num_root_segments; + } + _next = _current + result._size * BytesPerWord; + return result; + } + + AOTMapLogger::OopData obj_at(narrowOop* addr) override { + uint64_t n = (uint64_t)(*addr); + if (n == 0) { + return null_data(); + } else { + precond(n >= _buffer_start_narrow_oop); + address buffer_addr = _buffer_start + ((n - _buffer_start_narrow_oop) << _requested_shift); + return capture(buffer_addr); + } + } + + AOTMapLogger::OopData obj_at(oop* addr) override { + address requested_value = cast_from_oop
      (*addr); + if (requested_value == nullptr) { + return null_data(); + } else { + address buffer_addr = requested_value - _buffer_to_requested_delta; + return capture(buffer_addr); + } + } + + GrowableArrayCHeap* roots() override { + return new GrowableArrayCHeap(); + } +}; +#endif // INCLUDE_CDS_JAVA_HEAP + +#endif // SHARE_CDS_AOTMAPPEDHEAP_HPP diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.cpp b/src/hotspot/share/cds/aotMappedHeapLoader.cpp index 210867be70c..7a201d8297f 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.cpp @@ -23,6 +23,7 @@ */ #include "cds/aotLogging.hpp" +#include "cds/aotMappedHeap.hpp" #include "cds/aotMappedHeapLoader.inline.hpp" #include "cds/aotMappedHeapWriter.hpp" #include "cds/aotMetaspace.hpp" @@ -221,7 +222,7 @@ void AOTMappedHeapLoader::patch_embedded_pointers(FileMapInfo* info, // the heap object may be loaded at a different address at run time. This structure is used // to translate the dump time addresses for all objects in FileMapInfo::space_at(region_index) // to their runtime addresses. -struct LoadedArchiveHeapRegion { +struct AOTMappedHeapRegion { int _region_index; // index for FileMapInfo::space_at(index) size_t _region_size; // number of bytes in this region uintptr_t _dumptime_base; // The dump-time (decoded) address of the first object in this region @@ -232,7 +233,7 @@ struct LoadedArchiveHeapRegion { } }; -void AOTMappedHeapLoader::init_loaded_heap_relocation(LoadedArchiveHeapRegion* loaded_region) { +void AOTMappedHeapLoader::init_loaded_heap_relocation(AOTMappedHeapRegion* loaded_region) { _dumptime_base = loaded_region->_dumptime_base; _dumptime_top = loaded_region->top(); _runtime_offset = loaded_region->_runtime_offset; @@ -249,7 +250,7 @@ class AOTMappedHeapLoader::PatchLoadedRegionPointers: public BitMapClosure { uintptr_t _top; public: - PatchLoadedRegionPointers(narrowOop* start, LoadedArchiveHeapRegion* loaded_region) + PatchLoadedRegionPointers(narrowOop* start, AOTMappedHeapRegion* loaded_region) : _start(start), _offset(loaded_region->_runtime_offset), _base(loaded_region->_dumptime_base), @@ -270,7 +271,7 @@ class AOTMappedHeapLoader::PatchLoadedRegionPointers: public BitMapClosure { } }; -bool AOTMappedHeapLoader::init_loaded_region(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_region, +bool AOTMappedHeapLoader::init_loaded_region(FileMapInfo* mapinfo, AOTMappedHeapRegion* loaded_region, MemRegion& archive_space) { size_t total_bytes = 0; FileMapRegion* r = mapinfo->region_at(AOTMetaspace::hp); @@ -301,7 +302,7 @@ bool AOTMappedHeapLoader::init_loaded_region(FileMapInfo* mapinfo, LoadedArchive return true; } -bool AOTMappedHeapLoader::load_heap_region_impl(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_region, +bool AOTMappedHeapLoader::load_heap_region_impl(FileMapInfo* mapinfo, AOTMappedHeapRegion* loaded_region, uintptr_t load_address) { uintptr_t bitmap_base = (uintptr_t)mapinfo->map_bitmap_region(); if (bitmap_base == 0) { @@ -340,7 +341,7 @@ bool AOTMappedHeapLoader::load_heap_region(FileMapInfo* mapinfo) { assert(can_load(), "loaded heap for must be supported"); init_narrow_oop_decoding(mapinfo->narrow_oop_base(), mapinfo->narrow_oop_shift()); - LoadedArchiveHeapRegion loaded_region; + AOTMappedHeapRegion loaded_region; memset(&loaded_region, 0, sizeof(loaded_region)); MemRegion archive_space; @@ -733,40 +734,22 @@ void AOTMappedHeapLoader::dealloc_heap_region(FileMapInfo* info) { } AOTMapLogger::OopDataIterator* AOTMappedHeapLoader::oop_iterator(FileMapInfo* info, address buffer_start, address buffer_end) { - class MappedLoaderOopIterator : public AOTMapLogger::OopDataIterator { - private: - address _current; - address _next; - - address _buffer_start; - address _buffer_end; - uint64_t _buffer_start_narrow_oop; - intptr_t _buffer_to_requested_delta; - int _requested_shift; - - size_t _num_root_segments; - size_t _num_obj_arrays_logged; - + class MappedLoaderOopIterator : public AOTMappedHeapOopIterator { public: MappedLoaderOopIterator(address buffer_start, address buffer_end, - uint64_t buffer_start_narrow_oop, - intptr_t buffer_to_requested_delta, + address requested_base, + address requested_start, int requested_shift, - size_t num_root_segments) - : _current(nullptr), - _next(buffer_start), - _buffer_start(buffer_start), - _buffer_end(buffer_end), - _buffer_start_narrow_oop(buffer_start_narrow_oop), - _buffer_to_requested_delta(buffer_to_requested_delta), - _requested_shift(requested_shift), - _num_root_segments(num_root_segments), - _num_obj_arrays_logged(0) { - } + size_t num_root_segments) : + AOTMappedHeapOopIterator(buffer_start, + buffer_end, + requested_base, + requested_start, + requested_shift, + num_root_segments) {} - - AOTMapLogger::OopData capture(address buffered_addr) { + AOTMapLogger::OopData capture(address buffered_addr) override { oopDesc* raw_oop = (oopDesc*)buffered_addr; size_t size = raw_oop->size(); address requested_addr = buffered_addr + _buffer_to_requested_delta; @@ -784,62 +767,17 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapLoader::oop_iterator(FileMapInfo* in size, false }; } - - bool has_next() override { - return _next < _buffer_end; - } - - AOTMapLogger::OopData next() override { - _current = _next; - AOTMapLogger::OopData result = capture(_current); - if (result._klass->is_objArray_klass()) { - result._is_root_segment = _num_obj_arrays_logged++ < _num_root_segments; - } - _next = _current + result._size * BytesPerWord; - return result; - } - - AOTMapLogger::OopData obj_at(narrowOop* addr) override { - uint64_t n = (uint64_t)(*addr); - if (n == 0) { - return null_data(); - } else { - precond(n >= _buffer_start_narrow_oop); - address buffer_addr = _buffer_start + ((n - _buffer_start_narrow_oop) << _requested_shift); - return capture(buffer_addr); - } - } - - AOTMapLogger::OopData obj_at(oop* addr) override { - address requested_value = cast_from_oop
      (*addr); - if (requested_value == nullptr) { - return null_data(); - } else { - address buffer_addr = requested_value - _buffer_to_requested_delta; - return capture(buffer_addr); - } - } - - GrowableArrayCHeap* roots() override { - return new GrowableArrayCHeap(); - } }; FileMapRegion* r = info->region_at(AOTMetaspace::hp); address requested_base = UseCompressedOops ? (address)info->narrow_oop_base() : heap_region_requested_address(info); address requested_start = requested_base + r->mapping_offset(); int requested_shift = info->narrow_oop_shift(); - intptr_t buffer_to_requested_delta = requested_start - buffer_start; - uint64_t buffer_start_narrow_oop = 0xdeadbeed; - if (UseCompressedOops) { - buffer_start_narrow_oop = (uint64_t)(pointer_delta(requested_start, requested_base, 1)) >> requested_shift; - assert(buffer_start_narrow_oop < 0xffffffff, "sanity"); - } return new MappedLoaderOopIterator(buffer_start, buffer_end, - buffer_start_narrow_oop, - buffer_to_requested_delta, + requested_base, + requested_start, requested_shift, info->mapped_heap()->root_segments().count()); } diff --git a/src/hotspot/share/cds/aotMappedHeapLoader.hpp b/src/hotspot/share/cds/aotMappedHeapLoader.hpp index d344d7b0b0a..7c5ca1b1f9e 100644 --- a/src/hotspot/share/cds/aotMappedHeapLoader.hpp +++ b/src/hotspot/share/cds/aotMappedHeapLoader.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 @@ -37,8 +37,8 @@ #include "utilities/growableArray.hpp" #include "utilities/macros.hpp" +struct AOTMappedHeapRegion; class FileMapInfo; -struct LoadedArchiveHeapRegion; class AOTMappedHeapLoader : AllStatic { friend class AOTMapLogger; @@ -93,7 +93,7 @@ public: // function instead. inline static oop decode_from_archive(narrowOop v) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); - // More efficient version, but works only when ArchiveHeap is mapped. + // More efficient version, but works only when is_mapped() inline static oop decode_from_mapped_archive(narrowOop v) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); static void patch_compressed_embedded_pointers(BitMapView bm, @@ -113,7 +113,7 @@ private: static bool _is_loaded; // Support for loaded archived heap. These are cached values from - // LoadedArchiveHeapRegion's. + // AOTMappedHeapRegion's. static uintptr_t _dumptime_base; static uintptr_t _dumptime_top; static intx _runtime_offset; @@ -141,10 +141,10 @@ private: static bool _heap_pointers_need_patching; static void init_narrow_oop_decoding(address base, int shift); - static bool init_loaded_region(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_region, + static bool init_loaded_region(FileMapInfo* mapinfo, AOTMappedHeapRegion* loaded_region, MemRegion& archive_space); - static bool load_heap_region_impl(FileMapInfo* mapinfo, LoadedArchiveHeapRegion* loaded_region, uintptr_t buffer); - static void init_loaded_heap_relocation(LoadedArchiveHeapRegion* reloc_info); + static bool load_heap_region_impl(FileMapInfo* mapinfo, AOTMappedHeapRegion* loaded_region, uintptr_t buffer); + static void init_loaded_heap_relocation(AOTMappedHeapRegion* reloc_info); static void patch_native_pointers(); static void finish_loaded_heap(); static void verify_loaded_heap(); diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.cpp b/src/hotspot/share/cds/aotMappedHeapWriter.cpp index e73b980614a..64c0e3c40e8 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.cpp @@ -22,7 +22,7 @@ * */ -#include "cds/aotMappedHeapLoader.hpp" +#include "cds/aotMappedHeap.hpp" #include "cds/aotMappedHeapWriter.hpp" #include "cds/aotReferenceObjSupport.hpp" #include "cds/cdsConfig.hpp" @@ -151,7 +151,7 @@ void AOTMappedHeapWriter::add_source_obj(oop src_obj) { } void AOTMappedHeapWriter::write(GrowableArrayCHeap* roots, - ArchiveMappedHeapInfo* heap_info) { + AOTMappedHeapInfo* heap_info) { assert(CDSConfig::is_dumping_heap(), "sanity"); allocate_buffer(); copy_source_objs_to_buffer(roots); @@ -598,7 +598,7 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { // // So we just hard code it to NOCOOPS_REQUESTED_BASE. // -void AOTMappedHeapWriter::set_requested_address_range(ArchiveMappedHeapInfo* info) { +void AOTMappedHeapWriter::set_requested_address_range(AOTMappedHeapInfo* info) { assert(!info->is_used(), "only set once"); size_t heap_region_byte_size = _buffer_used; @@ -792,7 +792,7 @@ static void log_bitmap_usage(const char* which, BitMap* bitmap, size_t total_bit // Update all oop fields embedded in the buffered objects void AOTMappedHeapWriter::relocate_embedded_oops(GrowableArrayCHeap* roots, - ArchiveMappedHeapInfo* heap_info) { + AOTMappedHeapInfo* heap_info) { size_t oopmap_unit = (UseCompressedOops ? sizeof(narrowOop) : sizeof(oop)); size_t heap_region_byte_size = _buffer_used; heap_info->oopmap()->resize(heap_region_byte_size / oopmap_unit); @@ -862,7 +862,7 @@ void AOTMappedHeapWriter::mark_native_pointers(oop orig_obj) { }); } -void AOTMappedHeapWriter::compute_ptrmap(ArchiveMappedHeapInfo* heap_info) { +void AOTMappedHeapWriter::compute_ptrmap(AOTMappedHeapInfo* heap_info) { int num_non_null_ptrs = 0; Metadata** bottom = (Metadata**) _requested_bottom; Metadata** top = (Metadata**) _requested_top; // exclusive @@ -909,40 +909,23 @@ void AOTMappedHeapWriter::compute_ptrmap(ArchiveMappedHeapInfo* heap_info) { num_non_null_ptrs, size_t(heap_info->ptrmap()->size())); } -AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHeapInfo* heap_info) { - class MappedWriterOopIterator : public AOTMapLogger::OopDataIterator { - private: - address _current; - address _next; - - address _buffer_start; - address _buffer_end; - uint64_t _buffer_start_narrow_oop; - intptr_t _buffer_to_requested_delta; - int _requested_shift; - - size_t _num_root_segments; - size_t _num_obj_arrays_logged; - +AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(AOTMappedHeapInfo* heap_info) { + class MappedWriterOopIterator : public AOTMappedHeapOopIterator { public: MappedWriterOopIterator(address buffer_start, address buffer_end, - uint64_t buffer_start_narrow_oop, - intptr_t buffer_to_requested_delta, + address requested_base, + address requested_start, int requested_shift, - size_t num_root_segments) - : _current(nullptr), - _next(buffer_start), - _buffer_start(buffer_start), - _buffer_end(buffer_end), - _buffer_start_narrow_oop(buffer_start_narrow_oop), - _buffer_to_requested_delta(buffer_to_requested_delta), - _requested_shift(requested_shift), - _num_root_segments(num_root_segments), - _num_obj_arrays_logged(0) { - } + size_t num_root_segments) : + AOTMappedHeapOopIterator(buffer_start, + buffer_end, + requested_base, + requested_start, + requested_shift, + num_root_segments) {} - AOTMapLogger::OopData capture(address buffered_addr) { + AOTMapLogger::OopData capture(address buffered_addr) override { oopDesc* raw_oop = (oopDesc*)buffered_addr; size_t size = size_of_buffered_oop(buffered_addr); address requested_addr = buffered_addr_to_requested_addr(buffered_addr); @@ -960,45 +943,6 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHe size, false }; } - - bool has_next() override { - return _next < _buffer_end; - } - - AOTMapLogger::OopData next() override { - _current = _next; - AOTMapLogger::OopData result = capture(_current); - if (result._klass->is_objArray_klass()) { - result._is_root_segment = _num_obj_arrays_logged++ < _num_root_segments; - } - _next = _current + result._size * BytesPerWord; - return result; - } - - AOTMapLogger::OopData obj_at(narrowOop* addr) override { - uint64_t n = (uint64_t)(*addr); - if (n == 0) { - return null_data(); - } else { - precond(n >= _buffer_start_narrow_oop); - address buffer_addr = _buffer_start + ((n - _buffer_start_narrow_oop) << _requested_shift); - return capture(buffer_addr); - } - } - - AOTMapLogger::OopData obj_at(oop* addr) override { - address requested_value = cast_from_oop
      (*addr); - if (requested_value == nullptr) { - return null_data(); - } else { - address buffer_addr = requested_value - _buffer_to_requested_delta; - return capture(buffer_addr); - } - } - - GrowableArrayCHeap* roots() override { - return new GrowableArrayCHeap(); - } }; MemRegion r = heap_info->buffer_region(); @@ -1008,17 +952,11 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHe address requested_base = UseCompressedOops ? AOTMappedHeapWriter::narrow_oop_base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE; address requested_start = UseCompressedOops ? AOTMappedHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base; int requested_shift = AOTMappedHeapWriter::narrow_oop_shift(); - intptr_t buffer_to_requested_delta = requested_start - buffer_start; - uint64_t buffer_start_narrow_oop = 0xdeadbeed; - if (UseCompressedOops) { - buffer_start_narrow_oop = (uint64_t)(pointer_delta(requested_start, requested_base, 1)) >> requested_shift; - assert(buffer_start_narrow_oop < 0xffffffff, "sanity"); - } return new MappedWriterOopIterator(buffer_start, buffer_end, - buffer_start_narrow_oop, - buffer_to_requested_delta, + requested_base, + requested_start, requested_shift, heap_info->root_segments().count()); } diff --git a/src/hotspot/share/cds/aotMappedHeapWriter.hpp b/src/hotspot/share/cds/aotMappedHeapWriter.hpp index eafd38ac8bb..7481e7922a0 100644 --- a/src/hotspot/share/cds/aotMappedHeapWriter.hpp +++ b/src/hotspot/share/cds/aotMappedHeapWriter.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 @@ -196,10 +196,10 @@ private: static int filler_array_length(size_t fill_bytes); static HeapWord* init_filler_array_at_buffer_top(int array_length, size_t fill_bytes); - static void set_requested_address_range(ArchiveMappedHeapInfo* info); + static void set_requested_address_range(AOTMappedHeapInfo* info); static void mark_native_pointers(oop orig_obj); - static void relocate_embedded_oops(GrowableArrayCHeap* roots, ArchiveMappedHeapInfo* info); - static void compute_ptrmap(ArchiveMappedHeapInfo *info); + static void relocate_embedded_oops(GrowableArrayCHeap* roots, AOTMappedHeapInfo* info); + static void compute_ptrmap(AOTMappedHeapInfo *info); static bool is_in_requested_range(oop o); static oop requested_obj_from_buffer_offset(size_t offset); @@ -229,7 +229,7 @@ public: static bool is_string_too_large_to_archive(oop string); static bool is_dumped_interned_string(oop o); static void add_to_dumped_interned_strings(oop string); - static void write(GrowableArrayCHeap*, ArchiveMappedHeapInfo* heap_info); + static void write(GrowableArrayCHeap*, AOTMappedHeapInfo* heap_info); static address requested_address(); // requested address of the lowest achived heap object static size_t get_filler_size_at(address buffered_addr); @@ -240,7 +240,7 @@ public: static Klass* real_klass_of_buffered_oop(address buffered_addr); static size_t size_of_buffered_oop(address buffered_addr); - static AOTMapLogger::OopDataIterator* oop_iterator(ArchiveMappedHeapInfo* heap_info); + static AOTMapLogger::OopDataIterator* oop_iterator(AOTMappedHeapInfo* heap_info); }; #endif // INCLUDE_CDS_JAVA_HEAP #endif // SHARE_CDS_AOTMAPPEDHEAPWRITER_HPP diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 894a35183ca..8bb8387f1ab 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -661,8 +661,8 @@ void AOTMetaspace::rewrite_bytecodes_and_calculate_fingerprints(Thread* thread, class VM_PopulateDumpSharedSpace : public VM_Operation { private: - ArchiveMappedHeapInfo _mapped_heap_info; - ArchiveStreamedHeapInfo _streamed_heap_info; + AOTMappedHeapInfo _mapped_heap_info; + AOTStreamedHeapInfo _streamed_heap_info; FileMapInfo* _map_info; StaticArchiveBuilder& _builder; @@ -682,8 +682,8 @@ public: bool skip_operation() const { return false; } VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; } - ArchiveMappedHeapInfo* mapped_heap_info() { return &_mapped_heap_info; } - ArchiveStreamedHeapInfo* streamed_heap_info() { return &_streamed_heap_info; } + AOTMappedHeapInfo* mapped_heap_info() { return &_mapped_heap_info; } + AOTStreamedHeapInfo* streamed_heap_info() { return &_streamed_heap_info; } void doit(); // outline because gdb sucks bool allow_nested_vm_operations() const { return true; } }; // class VM_PopulateDumpSharedSpace @@ -1212,8 +1212,8 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS bool AOTMetaspace::write_static_archive(ArchiveBuilder* builder, FileMapInfo* map_info, - ArchiveMappedHeapInfo* mapped_heap_info, - ArchiveStreamedHeapInfo* streamed_heap_info) { + AOTMappedHeapInfo* mapped_heap_info, + AOTStreamedHeapInfo* streamed_heap_info) { // relocate the data so that it can be mapped to AOTMetaspace::requested_base_address() // without runtime relocation. builder->relocate_to_requested(); diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index ab78787288f..4607a936abe 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -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 @@ -33,8 +33,8 @@ #include "utilities/macros.hpp" class ArchiveBuilder; -class ArchiveMappedHeapInfo; -class ArchiveStreamedHeapInfo; +class AOTMappedHeapInfo; +class AOTStreamedHeapInfo; class FileMapInfo; class Method; class outputStream; @@ -192,8 +192,8 @@ private: static void open_output_mapinfo(); static bool write_static_archive(ArchiveBuilder* builder, FileMapInfo* map_info, - ArchiveMappedHeapInfo* mapped_heap_info, - ArchiveStreamedHeapInfo* streamed_heap_info); + AOTMappedHeapInfo* mapped_heap_info, + AOTStreamedHeapInfo* streamed_heap_info); static FileMapInfo* open_static_archive(); static FileMapInfo* open_dynamic_archive(); // use_requested_addr: If true (default), attempt to map at the address the diff --git a/src/hotspot/share/cds/aotStreamedHeap.cpp b/src/hotspot/share/cds/aotStreamedHeap.cpp new file mode 100644 index 00000000000..3378924bf32 --- /dev/null +++ b/src/hotspot/share/cds/aotStreamedHeap.cpp @@ -0,0 +1,55 @@ +/* + * 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 "cds/aotStreamedHeap.hpp" + +// Anything that goes in the header must be thoroughly purged from uninitialized memory +// as it will be written to disk. Therefore, the constructors memset the memory to 0. +// This is not the prettiest thing, but we need to know every byte is initialized, +// including potential padding between fields. + +AOTStreamedHeapHeader::AOTStreamedHeapHeader(size_t forwarding_offset, + size_t roots_offset, + size_t num_roots, + size_t root_highest_object_index_table_offset, + size_t num_archived_objects) { + memset((char*)this, 0, sizeof(*this)); + _forwarding_offset = forwarding_offset; + _roots_offset = roots_offset; + _num_roots = num_roots; + _root_highest_object_index_table_offset = root_highest_object_index_table_offset; + _num_archived_objects = num_archived_objects; +} + +AOTStreamedHeapHeader::AOTStreamedHeapHeader() { + memset((char*)this, 0, sizeof(*this)); +} + +AOTStreamedHeapHeader AOTStreamedHeapInfo::create_header() { + return AOTStreamedHeapHeader{_forwarding_offset, + _roots_offset, + _num_roots, + _root_highest_object_index_table_offset, + _num_archived_objects}; +} diff --git a/src/hotspot/share/cds/aotStreamedHeap.hpp b/src/hotspot/share/cds/aotStreamedHeap.hpp new file mode 100644 index 00000000000..f06b1bcb4c6 --- /dev/null +++ b/src/hotspot/share/cds/aotStreamedHeap.hpp @@ -0,0 +1,147 @@ +/* + * 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. + * + */ + +#ifndef SHARE_CDS_AOTSTREAMEDHEAP_HPP +#define SHARE_CDS_AOTSTREAMEDHEAP_HPP + +#include "cds/aotMapLogger.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/macros.hpp" + +class AOTStreamedHeapHeader { + size_t _forwarding_offset; // Offset of forwarding information in the heap region. + size_t _roots_offset; // Start position for the roots + size_t _root_highest_object_index_table_offset; // Offset of root dfs depth information + size_t _num_roots; // Number of embedded roots + size_t _num_archived_objects; // The number of archived heap objects + +public: + AOTStreamedHeapHeader(); + AOTStreamedHeapHeader(size_t forwarding_offset, + size_t roots_offset, + size_t num_roots, + size_t root_highest_object_index_table_offset, + size_t num_archived_objects); + + size_t forwarding_offset() const { return _forwarding_offset; } + size_t roots_offset() const { return _roots_offset; } + size_t num_roots() const { return _num_roots; } + size_t root_highest_object_index_table_offset() const { return _root_highest_object_index_table_offset; } + size_t num_archived_objects() const { return _num_archived_objects; } + + // This class is trivially copyable and assignable. + AOTStreamedHeapHeader(const AOTStreamedHeapHeader&) = default; + AOTStreamedHeapHeader& operator=(const AOTStreamedHeapHeader&) = default; +}; + +class AOTStreamedHeapInfo { + MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive. + CHeapBitMap _oopmap; + size_t _roots_offset; // Offset of the HeapShared::roots() object, from the bottom + // of the archived heap objects, in bytes. + size_t _num_roots; + + size_t _forwarding_offset; // Offset of forwarding information from the bottom + size_t _root_highest_object_index_table_offset; // Offset to root dfs depth information + size_t _num_archived_objects; // The number of archived objects written into the CDS archive. + +public: + AOTStreamedHeapInfo() + : _buffer_region(), + _oopmap(128, mtClassShared), + _roots_offset(), + _forwarding_offset(), + _root_highest_object_index_table_offset(), + _num_archived_objects() {} + + bool is_used() { return !_buffer_region.is_empty(); } + + void set_buffer_region(MemRegion r) { _buffer_region = r; } + MemRegion buffer_region() { return _buffer_region; } + char* buffer_start() { return (char*)_buffer_region.start(); } + size_t buffer_byte_size() { return _buffer_region.byte_size(); } + + CHeapBitMap* oopmap() { return &_oopmap; } + void set_roots_offset(size_t n) { _roots_offset = n; } + size_t roots_offset() { return _roots_offset; } + void set_num_roots(size_t n) { _num_roots = n; } + size_t num_roots() { return _num_roots; } + void set_forwarding_offset(size_t n) { _forwarding_offset = n; } + void set_root_highest_object_index_table_offset(size_t n) { _root_highest_object_index_table_offset = n; } + void set_num_archived_objects(size_t n) { _num_archived_objects = n; } + size_t num_archived_objects() { return _num_archived_objects; } + + AOTStreamedHeapHeader create_header(); +}; + +#if INCLUDE_CDS_JAVA_HEAP +class AOTStreamedHeapOopIterator : public AOTMapLogger::OopDataIterator { +protected: + int _current; + int _next; + address _buffer_start; + int _num_archived_objects; + +public: + AOTStreamedHeapOopIterator(address buffer_start, + int num_archived_objects) + : _current(0), + _next(1), + _buffer_start(buffer_start), + _num_archived_objects(num_archived_objects) {} + + virtual AOTMapLogger::OopData capture(int dfs_index) = 0; + + bool has_next() override { + return _next <= _num_archived_objects; + } + + AOTMapLogger::OopData next() override { + _current = _next; + AOTMapLogger::OopData result = capture(_current); + _next = _current + 1; + return result; + } + + AOTMapLogger::OopData obj_at(narrowOop* addr) override { + int dfs_index = (int)(*addr); + if (dfs_index == 0) { + return null_data(); + } else { + return capture(dfs_index); + } + } + + AOTMapLogger::OopData obj_at(oop* addr) override { + int dfs_index = (int)cast_from_oop(*addr); + if (dfs_index == 0) { + return null_data(); + } else { + return capture(dfs_index); + } + } +}; +#endif // INCLUDE_CDS_JAVA_HEAP + +#endif // SHARE_CDS_AOTSTREAMEDHEAP_HPP diff --git a/src/hotspot/share/cds/aotStreamedHeapLoader.cpp b/src/hotspot/share/cds/aotStreamedHeapLoader.cpp index 6719f9bf898..39f735543cd 100644 --- a/src/hotspot/share/cds/aotStreamedHeapLoader.cpp +++ b/src/hotspot/share/cds/aotStreamedHeapLoader.cpp @@ -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 @@ -1102,25 +1102,13 @@ void AOTStreamedHeapLoader::finish_initialization(FileMapInfo* static_mapinfo) { } AOTMapLogger::OopDataIterator* AOTStreamedHeapLoader::oop_iterator(FileMapInfo* info, address buffer_start, address buffer_end) { - class StreamedLoaderOopIterator : public AOTMapLogger::OopDataIterator { - private: - int _current; - int _next; - - address _buffer_start; - - int _num_archived_objects; - + class StreamedLoaderOopIterator : public AOTStreamedHeapOopIterator { public: StreamedLoaderOopIterator(address buffer_start, int num_archived_objects) - : _current(0), - _next(1), - _buffer_start(buffer_start), - _num_archived_objects(num_archived_objects) { - } + : AOTStreamedHeapOopIterator(buffer_start, num_archived_objects) {} - AOTMapLogger::OopData capture(int dfs_index) { + AOTMapLogger::OopData capture(int dfs_index) override { size_t buffered_offset = buffer_offset_for_object_index(dfs_index); address buffered_addr = _buffer_start + buffered_offset; oopDesc* raw_oop = (oopDesc*)buffered_addr; @@ -1142,35 +1130,6 @@ AOTMapLogger::OopDataIterator* AOTStreamedHeapLoader::oop_iterator(FileMapInfo* false }; } - bool has_next() override { - return _next <= _num_archived_objects; - } - - AOTMapLogger::OopData next() override { - _current = _next; - AOTMapLogger::OopData result = capture(_current); - _next = _current + 1; - return result; - } - - AOTMapLogger::OopData obj_at(narrowOop* addr) override { - int dfs_index = (int)(*addr); - if (dfs_index == 0) { - return null_data(); - } else { - return capture(dfs_index); - } - } - - AOTMapLogger::OopData obj_at(oop* addr) override { - int dfs_index = (int)cast_from_oop(*addr); - if (dfs_index == 0) { - return null_data(); - } else { - return capture(dfs_index); - } - } - GrowableArrayCHeap* roots() override { GrowableArrayCHeap* result = new GrowableArrayCHeap(); diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp index 16acebc7d8d..f52532b2f2a 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.cpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.cpp @@ -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 @@ -163,7 +163,7 @@ void AOTStreamedHeapWriter::order_source_objs(GrowableArrayCHeap* roots, - ArchiveStreamedHeapInfo* heap_info) { + AOTStreamedHeapInfo* heap_info) { assert(CDSConfig::is_dumping_heap(), "sanity"); allocate_buffer(); order_source_objs(roots); @@ -453,7 +453,7 @@ static void log_bitmap_usage(const char* which, BitMap* bitmap, size_t total_bit } // Update all oop fields embedded in the buffered objects -void AOTStreamedHeapWriter::map_embedded_oops(ArchiveStreamedHeapInfo* heap_info) { +void AOTStreamedHeapWriter::map_embedded_oops(AOTStreamedHeapInfo* heap_info) { size_t oopmap_unit = (UseCompressedOops ? sizeof(narrowOop) : sizeof(oop)); size_t heap_region_byte_size = _buffer_used; heap_info->oopmap()->resize(heap_region_byte_size / oopmap_unit); @@ -497,7 +497,7 @@ oop AOTStreamedHeapWriter::buffered_addr_to_source_obj(address buffered_addr) { return buffered_offset_to_source_obj(buffered_address_to_offset(buffered_addr)); } -void AOTStreamedHeapWriter::populate_archive_heap_info(ArchiveStreamedHeapInfo* info) { +void AOTStreamedHeapWriter::populate_archive_heap_info(AOTStreamedHeapInfo* info) { assert(!info->is_used(), "only set once"); size_t heap_region_byte_size = _buffer_used; @@ -512,15 +512,9 @@ void AOTStreamedHeapWriter::populate_archive_heap_info(ArchiveStreamedHeapInfo* info->set_num_archived_objects((size_t)_source_objs->length()); } -AOTMapLogger::OopDataIterator* AOTStreamedHeapWriter::oop_iterator(ArchiveStreamedHeapInfo* heap_info) { - class StreamedWriterOopIterator : public AOTMapLogger::OopDataIterator { +AOTMapLogger::OopDataIterator* AOTStreamedHeapWriter::oop_iterator(AOTStreamedHeapInfo* heap_info) { + class StreamedWriterOopIterator : public AOTStreamedHeapOopIterator { private: - int _current; - int _next; - - address _buffer_start; - - int _num_archived_objects; int _num_archived_roots; int* _roots; @@ -529,15 +523,11 @@ AOTMapLogger::OopDataIterator* AOTStreamedHeapWriter::oop_iterator(ArchiveStream int num_archived_objects, int num_archived_roots, int* roots) - : _current(0), - _next(1), - _buffer_start(buffer_start), - _num_archived_objects(num_archived_objects), + : AOTStreamedHeapOopIterator(buffer_start, num_archived_objects), _num_archived_roots(num_archived_roots), - _roots(roots) { - } + _roots(roots) {} - AOTMapLogger::OopData capture(int dfs_index) { + AOTMapLogger::OopData capture(int dfs_index) override { size_t buffered_offset = _dfs_to_archive_object_table[dfs_index]; address buffered_addr = _buffer_start + buffered_offset; oop src_obj = AOTStreamedHeapWriter::buffered_offset_to_source_obj(buffered_offset); @@ -561,35 +551,6 @@ AOTMapLogger::OopDataIterator* AOTStreamedHeapWriter::oop_iterator(ArchiveStream false }; } - bool has_next() override { - return _next <= _num_archived_objects; - } - - AOTMapLogger::OopData next() override { - _current = _next; - AOTMapLogger::OopData result = capture(_current); - _next = _current + 1; - return result; - } - - AOTMapLogger::OopData obj_at(narrowOop* addr) override { - int dfs_index = (int)(*addr); - if (dfs_index == 0) { - return null_data(); - } else { - return capture(dfs_index); - } - } - - AOTMapLogger::OopData obj_at(oop* addr) override { - int dfs_index = (int)cast_from_oop(*addr); - if (dfs_index == 0) { - return null_data(); - } else { - return capture(dfs_index); - } - } - GrowableArrayCHeap* roots() override { GrowableArrayCHeap* result = new GrowableArrayCHeap(); diff --git a/src/hotspot/share/cds/aotStreamedHeapWriter.hpp b/src/hotspot/share/cds/aotStreamedHeapWriter.hpp index bde82f8ce29..ab5aec0327b 100644 --- a/src/hotspot/share/cds/aotStreamedHeapWriter.hpp +++ b/src/hotspot/share/cds/aotStreamedHeapWriter.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 @@ -117,7 +117,7 @@ class AOTStreamedHeapWriter : AllStatic { static void copy_forwarding_to_buffer(); static void copy_roots_max_dfs_to_buffer(int roots_length); - static void map_embedded_oops(ArchiveStreamedHeapInfo* info); + static void map_embedded_oops(AOTStreamedHeapInfo* info); static bool is_in_requested_range(oop o); static oop requested_obj_from_buffer_offset(size_t offset); @@ -131,14 +131,14 @@ class AOTStreamedHeapWriter : AllStatic { static void update_header_for_buffered_addr(address buffered_addr, oop src_obj, Klass* src_klass); - static void populate_archive_heap_info(ArchiveStreamedHeapInfo* info); + static void populate_archive_heap_info(AOTStreamedHeapInfo* info); public: static void init() NOT_CDS_JAVA_HEAP_RETURN; static void delete_tables_with_raw_oops(); static void add_source_obj(oop src_obj); - static void write(GrowableArrayCHeap*, ArchiveStreamedHeapInfo* heap_info); + static void write(GrowableArrayCHeap*, AOTStreamedHeapInfo* heap_info); static address buffered_heap_roots_addr() { return offset_to_buffered_address
      (_roots_offset); } @@ -156,7 +156,7 @@ public: static oop buffered_offset_to_source_obj(size_t buffered_offset); static oop buffered_addr_to_source_obj(address buffered_addr); - static AOTMapLogger::OopDataIterator* oop_iterator(ArchiveStreamedHeapInfo* heap_info); + static AOTMapLogger::OopDataIterator* oop_iterator(AOTStreamedHeapInfo* heap_info); }; #endif // INCLUDE_CDS_JAVA_HEAP #endif // SHARE_CDS_AOTSTREAMEDHEAPWRITER_HPP diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index 9161980c4be..e65bd3985ac 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -1154,7 +1154,7 @@ void ArchiveBuilder::print_stats() { _alloc_stats.print_stats(int(_ro_region.used()), int(_rw_region.used())); } -void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info) { +void ArchiveBuilder::write_archive(FileMapInfo* mapinfo, AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info) { // Make sure NUM_CDS_REGIONS (exported in cds.h) agrees with // AOTMetaspace::n_regions (internal to hotspot). assert(NUM_CDS_REGIONS == AOTMetaspace::n_regions, "sanity"); @@ -1213,8 +1213,8 @@ void ArchiveBuilder::count_relocated_pointer(bool tagged, bool nulled) { } void ArchiveBuilder::print_region_stats(FileMapInfo *mapinfo, - ArchiveMappedHeapInfo* mapped_heap_info, - ArchiveStreamedHeapInfo* streamed_heap_info) { + AOTMappedHeapInfo* mapped_heap_info, + AOTStreamedHeapInfo* streamed_heap_info) { // Print statistics of all the regions const size_t bitmap_used = mapinfo->region_at(AOTMetaspace::bm)->used(); const size_t bitmap_reserved = mapinfo->region_at(AOTMetaspace::bm)->used_aligned(); diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp index 9de6c02edc5..2284dbf70f8 100644 --- a/src/hotspot/share/cds/archiveBuilder.hpp +++ b/src/hotspot/share/cds/archiveBuilder.hpp @@ -39,8 +39,8 @@ #include "utilities/hashTable.hpp" #include "utilities/resizableHashTable.hpp" -class ArchiveMappedHeapInfo; -class ArchiveStreamedHeapInfo; +class AOTMappedHeapInfo; +class AOTStreamedHeapInfo; class CHeapBitMap; class FileMapInfo; class Klass; @@ -247,8 +247,8 @@ private: } _relocated_ptr_info; void print_region_stats(FileMapInfo *map_info, - ArchiveMappedHeapInfo* mapped_heap_info, - ArchiveStreamedHeapInfo* streamed_heap_info); + AOTMappedHeapInfo* mapped_heap_info, + AOTStreamedHeapInfo* streamed_heap_info); void print_bitmap_region_stats(size_t size, size_t total_size); void print_heap_region_stats(char* start, size_t size, size_t total_size); @@ -438,8 +438,8 @@ public: void make_training_data_shareable(); void relocate_to_requested(); void write_archive(FileMapInfo* mapinfo, - ArchiveMappedHeapInfo* mapped_heap_info, - ArchiveStreamedHeapInfo* streamed_heap_info); + AOTMappedHeapInfo* mapped_heap_info, + AOTStreamedHeapInfo* streamed_heap_info); void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 0eeb96bb269..7cd736885ad 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -974,8 +974,8 @@ size_t FileMapInfo::remove_bitmap_zeros(CHeapBitMap* map) { char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, - ArchiveMappedHeapInfo* mapped_heap_info, - ArchiveStreamedHeapInfo* streamed_heap_info, + AOTMappedHeapInfo* mapped_heap_info, + AOTStreamedHeapInfo* streamed_heap_info, size_t &size_in_bytes) { size_t removed_rw_leading_zeros = remove_bitmap_zeros(rw_ptrmap); size_t removed_ro_leading_zeros = remove_bitmap_zeros(ro_ptrmap); @@ -1035,7 +1035,7 @@ char* FileMapInfo::write_bitmap_region(CHeapBitMap* rw_ptrmap, } #if INCLUDE_CDS_JAVA_HEAP -size_t FileMapInfo::write_mapped_heap_region(ArchiveMappedHeapInfo* heap_info) { +size_t FileMapInfo::write_mapped_heap_region(AOTMappedHeapInfo* heap_info) { char* buffer_start = heap_info->buffer_start(); size_t buffer_size = heap_info->buffer_byte_size(); write_region(AOTMetaspace::hp, buffer_start, buffer_size, false, false); @@ -1043,7 +1043,7 @@ size_t FileMapInfo::write_mapped_heap_region(ArchiveMappedHeapInfo* heap_info) { return buffer_size; } -size_t FileMapInfo::write_streamed_heap_region(ArchiveStreamedHeapInfo* heap_info) { +size_t FileMapInfo::write_streamed_heap_region(AOTStreamedHeapInfo* heap_info) { char* buffer_start = heap_info->buffer_start(); size_t buffer_size = heap_info->buffer_byte_size(); write_region(AOTMetaspace::hp, buffer_start, buffer_size, true, false); diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index fbd3c8e1681..ec7b58a6d19 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -25,7 +25,9 @@ #ifndef SHARE_CDS_FILEMAP_HPP #define SHARE_CDS_FILEMAP_HPP +#include "cds/aotMappedHeap.hpp" #include "cds/aotMetaspace.hpp" +#include "cds/aotStreamedHeap.hpp" #include "cds/archiveUtils.hpp" #include "cds/heapShared.hpp" #include "include/cds.h" @@ -144,8 +146,8 @@ private: size_t _rw_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the rw region size_t _ro_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the ro region - ArchiveMappedHeapHeader _mapped_heap_header; - ArchiveStreamedHeapHeader _streamed_heap_header; + AOTMappedHeapHeader _mapped_heap_header; + AOTStreamedHeapHeader _streamed_heap_header; // The following are parameters that affect MethodData layout. u1 _compiler_type; @@ -209,11 +211,11 @@ public: size_t ro_ptrmap_start_pos() const { return _ro_ptrmap_start_pos; } // Heap archiving - const ArchiveMappedHeapHeader* mapped_heap() const { return &_mapped_heap_header; } - const ArchiveStreamedHeapHeader* streamed_heap() const { return &_streamed_heap_header; } + const AOTMappedHeapHeader* mapped_heap() const { return &_mapped_heap_header; } + const AOTStreamedHeapHeader* streamed_heap() const { return &_streamed_heap_header; } - void set_streamed_heap_header(ArchiveStreamedHeapHeader header) { _streamed_heap_header = header; } - void set_mapped_heap_header(ArchiveMappedHeapHeader header) { _mapped_heap_header = header; } + void set_streamed_heap_header(AOTStreamedHeapHeader header) { _streamed_heap_header = header; } + void set_mapped_heap_header(AOTMappedHeapHeader header) { _mapped_heap_header = header; } void set_has_platform_or_app_classes(bool v) { _has_platform_or_app_classes = v; } void set_cloned_vtables(char* p) { set_as_offset(p, &_cloned_vtables_offset); } @@ -309,8 +311,8 @@ public: uintx max_heap_size() const { return header()->max_heap_size(); } size_t core_region_alignment() const { return header()->core_region_alignment(); } - const ArchiveMappedHeapHeader* mapped_heap() const { return header()->mapped_heap(); } - const ArchiveStreamedHeapHeader* streamed_heap() const { return header()->streamed_heap(); } + const AOTMappedHeapHeader* mapped_heap() const { return header()->mapped_heap(); } + const AOTStreamedHeapHeader* streamed_heap() const { return header()->streamed_heap(); } bool object_streaming_mode() const { return header()->object_streaming_mode(); } CompressedOops::Mode narrow_oop_mode() const { return header()->narrow_oop_mode(); } @@ -372,11 +374,11 @@ public: size_t remove_bitmap_zeros(CHeapBitMap* map); char* write_bitmap_region(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap, - ArchiveMappedHeapInfo* mapped_heap_info, - ArchiveStreamedHeapInfo* streamed_heap_info, + AOTMappedHeapInfo* mapped_heap_info, + AOTStreamedHeapInfo* streamed_heap_info, size_t &size_in_bytes); - size_t write_mapped_heap_region(ArchiveMappedHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN_(0); - size_t write_streamed_heap_region(ArchiveStreamedHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN_(0); + size_t write_mapped_heap_region(AOTMappedHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN_(0); + size_t write_streamed_heap_region(AOTStreamedHeapInfo* heap_info) NOT_CDS_JAVA_HEAP_RETURN_(0); void write_bytes(const void* buffer, size_t count); void write_bytes_aligned(const void* buffer, size_t count); size_t read_bytes(void* buffer, size_t count); diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 143f9147853..69a098c67b3 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -95,55 +95,6 @@ struct ArchivableStaticFieldInfo { } }; -// Anything that goes in the header must be thoroughly purged from uninitialized memory -// as it will be written to disk. Therefore, the constructors memset the memory to 0. -// This is not the prettiest thing, but we need to know every byte is initialized, -// including potential padding between fields. - -ArchiveMappedHeapHeader::ArchiveMappedHeapHeader(size_t ptrmap_start_pos, - size_t oopmap_start_pos, - HeapRootSegments root_segments) { - memset((char*)this, 0, sizeof(*this)); - _ptrmap_start_pos = ptrmap_start_pos; - _oopmap_start_pos = oopmap_start_pos; - _root_segments = root_segments; -} - -ArchiveMappedHeapHeader::ArchiveMappedHeapHeader() { - memset((char*)this, 0, sizeof(*this)); -} - -ArchiveMappedHeapHeader ArchiveMappedHeapInfo::create_header() { - return ArchiveMappedHeapHeader{_ptrmap_start_pos, - _oopmap_start_pos, - _root_segments}; -} - -ArchiveStreamedHeapHeader::ArchiveStreamedHeapHeader(size_t forwarding_offset, - size_t roots_offset, - size_t num_roots, - size_t root_highest_object_index_table_offset, - size_t num_archived_objects) { - memset((char*)this, 0, sizeof(*this)); - _forwarding_offset = forwarding_offset; - _roots_offset = roots_offset; - _num_roots = num_roots; - _root_highest_object_index_table_offset = root_highest_object_index_table_offset; - _num_archived_objects = num_archived_objects; -} - -ArchiveStreamedHeapHeader::ArchiveStreamedHeapHeader() { - memset((char*)this, 0, sizeof(*this)); -} - -ArchiveStreamedHeapHeader ArchiveStreamedHeapInfo::create_header() { - return ArchiveStreamedHeapHeader{_forwarding_offset, - _roots_offset, - _num_roots, - _root_highest_object_index_table_offset, - _num_archived_objects}; -} - HeapArchiveMode HeapShared::_heap_load_mode = HeapArchiveMode::_uninitialized; HeapArchiveMode HeapShared::_heap_write_mode = HeapArchiveMode::_uninitialized; @@ -892,7 +843,7 @@ void HeapShared::end_scanning_for_oops() { delete_seen_objects_table(); } -void HeapShared::write_heap(ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info) { +void HeapShared::write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info) { { NoSafepointVerifier nsv; CDSHeapVerifier::verify(); diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index 3c7068e96ab..ba17ddda267 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -142,129 +142,6 @@ enum class HeapArchiveMode { _streaming }; -class ArchiveMappedHeapHeader { - size_t _ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the heap. - size_t _oopmap_start_pos; // The first bit in the oopmap corresponds to this position in the heap. - HeapRootSegments _root_segments; // Heap root segments info - -public: - ArchiveMappedHeapHeader(); - ArchiveMappedHeapHeader(size_t ptrmap_start_pos, - size_t oopmap_start_pos, - HeapRootSegments root_segments); - - size_t ptrmap_start_pos() const { return _ptrmap_start_pos; } - size_t oopmap_start_pos() const { return _oopmap_start_pos; } - HeapRootSegments root_segments() const { return _root_segments; } - - // This class is trivially copyable and assignable. - ArchiveMappedHeapHeader(const ArchiveMappedHeapHeader&) = default; - ArchiveMappedHeapHeader& operator=(const ArchiveMappedHeapHeader&) = default; -}; - - -class ArchiveStreamedHeapHeader { - size_t _forwarding_offset; // Offset of forwarding information in the heap region. - size_t _roots_offset; // Start position for the roots - size_t _root_highest_object_index_table_offset; // Offset of root dfs depth information - size_t _num_roots; // Number of embedded roots - size_t _num_archived_objects; // The number of archived heap objects - -public: - ArchiveStreamedHeapHeader(); - ArchiveStreamedHeapHeader(size_t forwarding_offset, - size_t roots_offset, - size_t num_roots, - size_t root_highest_object_index_table_offset, - size_t num_archived_objects); - - size_t forwarding_offset() const { return _forwarding_offset; } - size_t roots_offset() const { return _roots_offset; } - size_t num_roots() const { return _num_roots; } - size_t root_highest_object_index_table_offset() const { return _root_highest_object_index_table_offset; } - size_t num_archived_objects() const { return _num_archived_objects; } - - // This class is trivially copyable and assignable. - ArchiveStreamedHeapHeader(const ArchiveStreamedHeapHeader&) = default; - ArchiveStreamedHeapHeader& operator=(const ArchiveStreamedHeapHeader&) = default; -}; - -class ArchiveMappedHeapInfo { - MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive. - CHeapBitMap _oopmap; - CHeapBitMap _ptrmap; - HeapRootSegments _root_segments; - size_t _oopmap_start_pos; // How many zeros were removed from the beginning of the bit map? - size_t _ptrmap_start_pos; // How many zeros were removed from the beginning of the bit map? - -public: - ArchiveMappedHeapInfo() : - _buffer_region(), - _oopmap(128, mtClassShared), - _ptrmap(128, mtClassShared), - _root_segments(), - _oopmap_start_pos(), - _ptrmap_start_pos() {} - bool is_used() { return !_buffer_region.is_empty(); } - - MemRegion buffer_region() { return _buffer_region; } - void set_buffer_region(MemRegion r) { _buffer_region = r; } - - char* buffer_start() { return (char*)_buffer_region.start(); } - size_t buffer_byte_size() { return _buffer_region.byte_size(); } - - CHeapBitMap* oopmap() { return &_oopmap; } - CHeapBitMap* ptrmap() { return &_ptrmap; } - - void set_oopmap_start_pos(size_t start_pos) { _oopmap_start_pos = start_pos; } - void set_ptrmap_start_pos(size_t start_pos) { _ptrmap_start_pos = start_pos; } - - void set_root_segments(HeapRootSegments segments) { _root_segments = segments; }; - HeapRootSegments root_segments() { return _root_segments; } - - ArchiveMappedHeapHeader create_header(); -}; - -class ArchiveStreamedHeapInfo { - MemRegion _buffer_region; // Contains the archived objects to be written into the CDS archive. - CHeapBitMap _oopmap; - size_t _roots_offset; // Offset of the HeapShared::roots() object, from the bottom - // of the archived heap objects, in bytes. - size_t _num_roots; - - size_t _forwarding_offset; // Offset of forwarding information from the bottom - size_t _root_highest_object_index_table_offset; // Offset to root dfs depth information - size_t _num_archived_objects; // The number of archived objects written into the CDS archive. - -public: - ArchiveStreamedHeapInfo() - : _buffer_region(), - _oopmap(128, mtClassShared), - _roots_offset(), - _forwarding_offset(), - _root_highest_object_index_table_offset(), - _num_archived_objects() {} - - bool is_used() { return !_buffer_region.is_empty(); } - - void set_buffer_region(MemRegion r) { _buffer_region = r; } - MemRegion buffer_region() { return _buffer_region; } - char* buffer_start() { return (char*)_buffer_region.start(); } - size_t buffer_byte_size() { return _buffer_region.byte_size(); } - - CHeapBitMap* oopmap() { return &_oopmap; } - void set_roots_offset(size_t n) { _roots_offset = n; } - size_t roots_offset() { return _roots_offset; } - void set_num_roots(size_t n) { _num_roots = n; } - size_t num_roots() { return _num_roots; } - void set_forwarding_offset(size_t n) { _forwarding_offset = n; } - void set_root_highest_object_index_table_offset(size_t n) { _root_highest_object_index_table_offset = n; } - void set_num_archived_objects(size_t n) { _num_archived_objects = n; } - size_t num_archived_objects() { return _num_archived_objects; } - - ArchiveStreamedHeapHeader create_header(); -}; - class HeapShared: AllStatic { friend class VerifySharedOopClosure; @@ -575,7 +452,7 @@ private: public: static void finish_materialize_objects() NOT_CDS_JAVA_HEAP_RETURN; - static void write_heap(ArchiveMappedHeapInfo* mapped_heap_info, ArchiveStreamedHeapInfo* streamed_heap_info) NOT_CDS_JAVA_HEAP_RETURN; + static void write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeapInfo* streamed_heap_info) NOT_CDS_JAVA_HEAP_RETURN; static objArrayOop scratch_resolved_references(ConstantPool* src); static void add_scratch_resolved_references(ConstantPool* src, objArrayOop dest) NOT_CDS_JAVA_HEAP_RETURN; static void init_dumping() NOT_CDS_JAVA_HEAP_RETURN; From 3de6dbab14e950c1725a48686478e4155c8d93c7 Mon Sep 17 00:00:00 2001 From: Damon Nguyen Date: Wed, 11 Feb 2026 00:55:17 +0000 Subject: [PATCH 202/215] 8377183: Impossible or redundant condition in AwtFrame::_NotifyModalBlocked of awt_Frame.cpp:1635 Reviewed-by: serb, prr --- src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp index e679eb88dd2..19a38bcaa2d 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 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 @@ -1632,7 +1632,7 @@ void AwtFrame::_NotifyModalBlocked(void *param) } } - if ((f != NULL) && ::IsWindow(f->GetHWnd())) + if (::IsWindow(f->GetHWnd())) { // get an HWND of the toplevel window this embedded frame is within HWND fHWnd = f->GetHWnd(); From 4e3033f2122d773c173b0bb50120099589adcf3c Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 11 Feb 2026 00:59:20 +0000 Subject: [PATCH 203/215] 8332189: Enable -Wzero-as-null-pointer-constant for gcc/clang Reviewed-by: azafari, dholmes, erikj --- doc/hotspot-style.html | 5 ++-- doc/hotspot-style.md | 3 +-- make/autoconf/flags-cflags.m4 | 47 +++++++++++++++++++---------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/doc/hotspot-style.html b/doc/hotspot-style.html index 362245cd00a..c7126622c7d 100644 --- a/doc/hotspot-style.html +++ b/doc/hotspot-style.html @@ -965,9 +965,8 @@ rather than NULL. See the paper for reasons to avoid NULL.

      Don't use (constant expression or literal) 0 for pointers. Note that C++14 removed non-literal 0 constants from null pointer -constants, though some compilers continue to treat them as such. -For historical reasons there may be lingering uses of 0 as a -pointer.

      +constants, though some compilers continue to treat them as +such.

      <atomic>

      Do not use facilities provided by the <atomic> header ( Date: Wed, 11 Feb 2026 01:12:06 +0000 Subject: [PATCH 204/215] 8377368: [REDO] Mixed jstack cannot find function in vDSO Reviewed-by: cjplummer, kevinw --- .../linux/native/libsaproc/libproc_impl.h | 5 +- .../linux/native/libsaproc/ps_core.c | 74 ++++++++++++-- .../sa/LingeredAppWithVDSOCall.java | 61 ++++++++++++ .../TestJhsdbJstackMixedWithVDSOCallCore.java | 96 +++++++++++++++++++ test/lib/jdk/test/lib/apps/LingeredApp.java | 16 +++- 5 files changed, 241 insertions(+), 11 deletions(-) create mode 100644 test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java create mode 100644 test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h index 42a6212510c..262e99f4a64 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/libproc_impl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, 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 @@ -95,6 +95,9 @@ struct core_data { // part of the class sharing workaround int classes_jsa_fd; // file descriptor of class share archive uintptr_t dynamic_addr; // address of dynamic section of a.out + uintptr_t vdso_addr; // address of vDSO + off64_t vdso_offset; // offset of vDSO in core + size_t vdso_size; // size of vDSO uintptr_t ld_base_addr; // base address of ld.so size_t num_maps; // number of maps. map_info* maps; // maps in a linked list diff --git a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c index 899d42152d1..6a991b18c10 100644 --- a/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c +++ b/src/jdk.hotspot.agent/linux/native/libsaproc/ps_core.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, 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 @@ -26,11 +26,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include "libproc_impl.h" #include "ps_core_common.h" #include "proc_service.h" @@ -285,6 +288,8 @@ static bool core_handle_note(struct ps_prochandle* ph, ELF_PHDR* note_phdr) { // We will adjust it in read_exec_segments(). ph->core->dynamic_addr = auxv->a_un.a_val; break; + } else if (auxv->a_type == AT_SYSINFO_EHDR) { + ph->core->vdso_addr = auxv->a_un.a_val; } auxv++; } @@ -350,6 +355,10 @@ static bool read_core_segments(struct ps_prochandle* ph, ELF_EHDR* core_ehdr) { print_error("failed to add map info\n"); goto err; } + if (core_php->p_vaddr == ph->core->vdso_addr) { + ph->core->vdso_offset = core_php->p_offset; + ph->core->vdso_size = core_php->p_memsz; + } } break; } @@ -413,10 +422,18 @@ static bool read_lib_segments(struct ps_prochandle* ph, int lib_fd, ELF_EHDR* li if (target_vaddr > existing_map->vaddr) { lib_memsz += target_vaddr - existing_map->vaddr; } + uint64_t aligned_lib_memsz = ROUNDUP(lib_memsz, page_size); + size_t aligned_segment_sz = ROUNDUP(lib_php->p_memsz, page_size); + if (target_vaddr == ph->core->vdso_addr) { + // We can overwrite with entire vDSO because vDSO on memory is + // entire ELF binary. + aligned_lib_memsz = ROUNDUP(existing_map->memsz, page_size); + aligned_segment_sz = aligned_lib_memsz; + } if ((existing_map->memsz != page_size) && (existing_map->fd != lib_fd) && - (ROUNDUP(existing_map->memsz, page_size) != ROUNDUP(lib_memsz, page_size))) { + (ROUNDUP(existing_map->memsz, page_size) != aligned_lib_memsz)) { print_error("address conflict @ 0x%lx (existing map size = %ld, size = %ld, flags = %d)\n", target_vaddr, existing_map->memsz, lib_php->p_memsz, lib_php->p_flags); @@ -425,11 +442,11 @@ static bool read_lib_segments(struct ps_prochandle* ph, int lib_fd, ELF_EHDR* li /* replace PT_LOAD segment with library segment */ print_debug("overwrote with new address mapping (memsz %ld -> %ld)\n", - existing_map->memsz, ROUNDUP(lib_php->p_memsz, page_size)); + existing_map->memsz, aligned_segment_sz); existing_map->fd = lib_fd; existing_map->offset = lib_php->p_offset; - existing_map->memsz = ROUNDUP(lib_php->p_memsz, page_size); + existing_map->memsz = aligned_segment_sz; } } @@ -593,6 +610,44 @@ static uintptr_t calc_prelinked_load_address(struct ps_prochandle* ph, int lib_f return load_addr; } +// Check for vDSO binary in kernel directory (/lib/modules//vdso), +// rewrite the given lib_name string if found. +// Otherwise copy vDSO memory in coredump to temporal file generated by tmpfile(). +// Returns FD for vDSO (should be closed by caller), or -1 on error. +static int handle_vdso(struct ps_prochandle* ph, char* lib_name, size_t lib_name_len) { + int lib_fd; + struct utsname uts; + uname(&uts); + + // Check vDSO binary first (for referring debuginfo if possible). + char *vdso_path = (char*)malloc(lib_name_len); + snprintf(vdso_path, lib_name_len, "/lib/modules/%s/vdso/vdso64.so", uts.release); + lib_fd = pathmap_open(vdso_path); + if (lib_fd != -1) { + print_debug("replace vDSO: %s -> %s\n", lib_name, vdso_path); + strncpy(lib_name, vdso_path, lib_name_len); + } else { + // Copy vDSO memory segment from core to temporal memory + // if vDSO binary is not available. + FILE* tmpf = tmpfile(); + if (tmpf == NULL) { + print_debug("can't create tmpfile for vDSO (%d)\n", errno); + lib_fd = -1; + } else { + lib_fd = fileno(tmpf); + off64_t ofs = ph->core->vdso_offset; + if (sendfile64(lib_fd, ph->core->core_fd, &ofs, ph->core->vdso_size) == -1) { + print_debug("can't copy vDSO (%d)\n", errno); + fclose(tmpf); + lib_fd = -1; + } + } + } + + free(vdso_path); + return lib_fd; +} + // read shared library info from runtime linker's data structures. // This work is done by librtlb_db in Solaris static bool read_shared_lib_info(struct ps_prochandle* ph) { @@ -687,9 +742,14 @@ static bool read_shared_lib_info(struct ps_prochandle* ph) { // it will fail later. } - if (lib_name[0] != '\0') { - // ignore empty lib names - lib_fd = pathmap_open(lib_name); + if (lib_name[0] != '\0') { // ignore empty lib names + // We can use lib_base_diff to compare with vdso_addr + // because base address of vDSO should be 0. + if (lib_base_diff == ph->core->vdso_addr) { + lib_fd = handle_vdso(ph, lib_name, sizeof(lib_name)); + } else { + lib_fd = pathmap_open(lib_name); + } if (lib_fd < 0) { print_debug("can't open shared object %s\n", lib_name); diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java new file mode 100644 index 00000000000..d893ddf6f89 --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithVDSOCall.java @@ -0,0 +1,61 @@ + +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.invoke.MethodHandle; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.ValueLayout; + +import jdk.test.lib.Asserts; +import jdk.test.lib.apps.LingeredApp; + + +public class LingeredAppWithVDSOCall extends LingeredApp { + + private static final MethodHandle gettimeofday; + + static { + var desc = FunctionDescriptor.of(ValueLayout.JAVA_INT, // return + ValueLayout.JAVA_LONG, // tv + ValueLayout.JAVA_LONG); // tz + var linker = Linker.nativeLinker(); + var gettimeofdayPtr = linker.defaultLookup().findOrThrow("gettimeofday"); + gettimeofday = linker.downcallHandle(gettimeofdayPtr, desc); + } + + private static void crashAtGettimeofday(long tvAddr, long tzAddr) { + try { + gettimeofday.invoke(tvAddr, tzAddr); + } catch (Throwable t) { + throw new RuntimeException(t); + } + Asserts.fail("gettimeofday() didn't crash"); + } + + public static void main(String[] args) { + setCrasher(() -> crashAtGettimeofday(100L, 200L)); + LingeredApp.main(args); + } +} diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java new file mode 100644 index 00000000000..84da46e272f --- /dev/null +++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackMixedWithVDSOCallCore.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, NTT DATA + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; + +import jtreg.SkippedException; + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.JDKToolLauncher; +import jdk.test.lib.Platform; +import jdk.test.lib.SA.SATestUtils; +import jdk.test.lib.Utils; +import jdk.test.lib.apps.LingeredApp; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.util.CoreUtils; + +/** + * @test + * @bug 8376269 + * @requires (os.family == "linux") & (vm.hasSA) + * @requires os.arch == "amd64" + * @library /test/lib + * @run driver TestJhsdbJstackMixedWithVDSOCallCore + */ +public class TestJhsdbJstackMixedWithVDSOCallCore { + + private static void runJstackMixed(String coreFileName) throws Exception { + JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb"); + launcher.addVMArgs(Utils.getTestJavaOpts()); + launcher.addToolArg("jstack"); + launcher.addToolArg("--mixed"); + launcher.addToolArg("--exe"); + launcher.addToolArg(JDKToolFinder.getTestJDKTool("java")); + launcher.addToolArg("--core"); + launcher.addToolArg(coreFileName); + + ProcessBuilder pb = SATestUtils.createProcessBuilder(launcher); + Process jhsdb = pb.start(); + OutputAnalyzer out = new OutputAnalyzer(jhsdb); + + jhsdb.waitFor(); + + System.out.println(out.getStdout()); + System.err.println(out.getStderr()); + + out.shouldContain("vdso_gettimeofday"); + } + + private static void checkVDSODebugInfo() { + var kernelVersion = System.getProperty("os.version"); + var vdso = Path.of("/lib", "modules", kernelVersion, "vdso", "vdso64.so"); + if (SATestUtils.getDebugInfo(vdso.toString()) == null) { + // Skip this test if debuginfo of vDSO not found because internal + // function of gettimeofday() would not be exported, and vDSO + // binary might be stripped. + throw new SkippedException("vDSO debuginfo not found (" + vdso.toString() + ")"); + } + } + + public static void main(String... args) throws Throwable { + if (Platform.isMusl()) { + throw new SkippedException("This test does not work on musl libc."); + } + checkVDSODebugInfo(); + + var app = new LingeredAppWithVDSOCall(); + app.setForceCrash(true); + LingeredApp.startApp(app, CoreUtils.getAlwaysPretouchArg(true)); + app.waitAppTerminate(); + + String crashOutput = app.getOutput().getStdout(); + String coreFileName = CoreUtils.getCoreFileLocation(crashOutput, app.getPid()); + runJstackMixed(coreFileName); + } +} diff --git a/test/lib/jdk/test/lib/apps/LingeredApp.java b/test/lib/jdk/test/lib/apps/LingeredApp.java index 13008e68c54..38ad9ae5b0e 100644 --- a/test/lib/jdk/test/lib/apps/LingeredApp.java +++ b/test/lib/jdk/test/lib/apps/LingeredApp.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 @@ -122,6 +122,12 @@ public class LingeredApp { this.forceCrash = forceCrash; } + private static Runnable crasher; + + public static void setCrasher(Runnable runnable) { + crasher = runnable; + } + native private static int crash(); /** @@ -628,8 +634,12 @@ public class LingeredApp { synchronized(steadyStateObj) { startSteadyStateThread(steadyStateObj); if (forceCrash) { - System.loadLibrary("LingeredApp"); // location of native crash() method - crash(); + if (crasher == null) { + System.loadLibrary("LingeredApp"); // location of native crash() method + crash(); + } else { + crasher.run(); + } } while (Files.exists(path)) { // Touch the lock to indicate our readiness From 8455b668104f97bc152985299a7814646c9fb1fd Mon Sep 17 00:00:00 2001 From: Ben Taylor Date: Wed, 11 Feb 2026 01:39:13 +0000 Subject: [PATCH 205/215] 8377200: Shenandoah: Convert shenandoahSharedVariables and related code to use Atomic Reviewed-by: wkemper, xpeng --- .../shenandoah/shenandoahSharedVariables.hpp | 78 ++++++++----------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp index 12c01ad5c90..e23187a5d3f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSharedVariables.hpp @@ -27,7 +27,7 @@ #include "gc/shenandoah/shenandoahPadding.hpp" #include "memory/allocation.hpp" -#include "runtime/atomicAccess.hpp" +#include "runtime/atomic.hpp" typedef int32_t ShenandoahSharedValue; typedef struct ShenandoahSharedFlag { @@ -37,7 +37,7 @@ typedef struct ShenandoahSharedFlag { }; shenandoah_padding(0); - volatile ShenandoahSharedValue value; + Atomic value; shenandoah_padding(1); ShenandoahSharedFlag() { @@ -45,19 +45,19 @@ typedef struct ShenandoahSharedFlag { } void set() { - AtomicAccess::release_store_fence(&value, (ShenandoahSharedValue)SET); + value.release_store_fence((ShenandoahSharedValue)SET); } void unset() { - AtomicAccess::release_store_fence(&value, (ShenandoahSharedValue)UNSET); + value.release_store_fence((ShenandoahSharedValue)UNSET); } bool is_set() const { - return AtomicAccess::load_acquire(&value) == SET; + return value.load_acquire() == SET; } bool is_unset() const { - return AtomicAccess::load_acquire(&value) == UNSET; + return value.load_acquire() == UNSET; } void set_cond(bool val) { @@ -72,7 +72,7 @@ typedef struct ShenandoahSharedFlag { if (is_set()) { return false; } - ShenandoahSharedValue old = AtomicAccess::cmpxchg(&value, (ShenandoahSharedValue)UNSET, (ShenandoahSharedValue)SET); + ShenandoahSharedValue old = value.compare_exchange((ShenandoahSharedValue)UNSET, (ShenandoahSharedValue)SET); return old == UNSET; // success } @@ -80,17 +80,13 @@ typedef struct ShenandoahSharedFlag { if (!is_set()) { return false; } - ShenandoahSharedValue old = AtomicAccess::cmpxchg(&value, (ShenandoahSharedValue)SET, (ShenandoahSharedValue)UNSET); + ShenandoahSharedValue old = value.compare_exchange((ShenandoahSharedValue)SET, (ShenandoahSharedValue)UNSET); return old == SET; // success } - volatile ShenandoahSharedValue* addr_of() { - return &value; - } - private: volatile ShenandoahSharedValue* operator&() { - fatal("Use addr_of() instead"); + fatal("Not supported"); return nullptr; } @@ -105,7 +101,7 @@ private: typedef struct ShenandoahSharedBitmap { shenandoah_padding(0); - volatile ShenandoahSharedValue value; + Atomic value; shenandoah_padding(1); ShenandoahSharedBitmap() { @@ -116,7 +112,7 @@ typedef struct ShenandoahSharedBitmap { assert (mask < (sizeof(ShenandoahSharedValue) * CHAR_MAX), "sanity"); ShenandoahSharedValue mask_val = (ShenandoahSharedValue) mask; while (true) { - ShenandoahSharedValue ov = AtomicAccess::load_acquire(&value); + ShenandoahSharedValue ov = value.load_acquire(); // We require all bits of mask_val to be set if ((ov & mask_val) == mask_val) { // already set @@ -124,7 +120,7 @@ typedef struct ShenandoahSharedBitmap { } ShenandoahSharedValue nv = ov | mask_val; - if (AtomicAccess::cmpxchg(&value, ov, nv) == ov) { + if (value.compare_exchange(ov, nv) == ov) { // successfully set: if value returned from cmpxchg equals ov, then nv has overwritten value. return; } @@ -135,14 +131,14 @@ typedef struct ShenandoahSharedBitmap { assert (mask < (sizeof(ShenandoahSharedValue) * CHAR_MAX), "sanity"); ShenandoahSharedValue mask_val = (ShenandoahSharedValue) mask; while (true) { - ShenandoahSharedValue ov = AtomicAccess::load_acquire(&value); + ShenandoahSharedValue ov = value.load_acquire(); if ((ov & mask_val) == 0) { // already unset return; } ShenandoahSharedValue nv = ov & ~mask_val; - if (AtomicAccess::cmpxchg(&value, ov, nv) == ov) { + if (value.compare_exchange(ov, nv) == ov) { // successfully unset return; } @@ -150,7 +146,7 @@ typedef struct ShenandoahSharedBitmap { } void clear() { - AtomicAccess::release_store_fence(&value, (ShenandoahSharedValue)0); + value.release_store_fence((ShenandoahSharedValue)0); } // Returns true iff any bit set in mask is set in this.value. @@ -161,18 +157,18 @@ typedef struct ShenandoahSharedBitmap { // Returns true iff all bits set in mask are set in this.value. bool is_set_exactly(uint mask) const { assert (mask < (sizeof(ShenandoahSharedValue) * CHAR_MAX), "sanity"); - uint uvalue = AtomicAccess::load_acquire(&value); + uint uvalue = value.load_acquire(); return (uvalue & mask) == mask; } // Returns true iff all bits set in mask are unset in this.value. bool is_unset(uint mask) const { assert (mask < (sizeof(ShenandoahSharedValue) * CHAR_MAX), "sanity"); - return (AtomicAccess::load_acquire(&value) & (ShenandoahSharedValue) mask) == 0; + return (value.load_acquire() & (ShenandoahSharedValue) mask) == 0; } bool is_clear() const { - return (AtomicAccess::load_acquire(&value)) == 0; + return (value.load_acquire()) == 0; } void set_cond(uint mask, bool val) { @@ -183,17 +179,13 @@ typedef struct ShenandoahSharedBitmap { } } - volatile ShenandoahSharedValue* addr_of() { - return &value; - } - ShenandoahSharedValue raw_value() const { - return value; + return value.load_relaxed(); } private: volatile ShenandoahSharedValue* operator&() { - fatal("Use addr_of() instead"); + fatal("Not supported"); return nullptr; } @@ -210,42 +202,36 @@ template struct ShenandoahSharedEnumFlag { typedef uint32_t EnumValueType; shenandoah_padding(0); - volatile EnumValueType value; + Atomic value; shenandoah_padding(1); - ShenandoahSharedEnumFlag() { - value = 0; - } + ShenandoahSharedEnumFlag() : value(0) {} void set(T v) { assert (v >= 0, "sanity"); assert (v < (sizeof(EnumValueType) * CHAR_MAX), "sanity"); - AtomicAccess::release_store_fence(&value, (EnumValueType)v); + value.release_store_fence((EnumValueType)v); } T get() const { - return (T)AtomicAccess::load_acquire(&value); + return (T)value.load_acquire(); } T cmpxchg(T new_value, T expected) { assert (new_value >= 0, "sanity"); assert (new_value < (sizeof(EnumValueType) * CHAR_MAX), "sanity"); - return (T)AtomicAccess::cmpxchg(&value, (EnumValueType)expected, (EnumValueType)new_value); + return (T)value.compare_exchange((EnumValueType)expected, (EnumValueType)new_value); } T xchg(T new_value) { assert (new_value >= 0, "sanity"); assert (new_value < (sizeof(EnumValueType) * CHAR_MAX), "sanity"); - return (T)AtomicAccess::xchg(&value, (EnumValueType)new_value); - } - - volatile EnumValueType* addr_of() { - return &value; + return (T)value.exchange((EnumValueType)new_value); } private: volatile T* operator&() { - fatal("Use addr_of() instead"); + fatal("Not supported"); return nullptr; } @@ -260,7 +246,7 @@ private: typedef struct ShenandoahSharedSemaphore { shenandoah_padding(0); - volatile ShenandoahSharedValue value; + Atomic value; shenandoah_padding(1); static uint max_tokens() { @@ -269,17 +255,17 @@ typedef struct ShenandoahSharedSemaphore { ShenandoahSharedSemaphore(uint tokens) { assert(tokens <= max_tokens(), "sanity"); - AtomicAccess::release_store_fence(&value, (ShenandoahSharedValue)tokens); + value.release_store_fence((ShenandoahSharedValue)tokens); } bool try_acquire() { while (true) { - ShenandoahSharedValue ov = AtomicAccess::load_acquire(&value); + ShenandoahSharedValue ov = value.load_acquire(); if (ov == 0) { return false; } ShenandoahSharedValue nv = ov - 1; - if (AtomicAccess::cmpxchg(&value, ov, nv) == ov) { + if (value.compare_exchange(ov, nv) == ov) { // successfully set return true; } @@ -287,7 +273,7 @@ typedef struct ShenandoahSharedSemaphore { } void claim_all() { - AtomicAccess::release_store_fence(&value, (ShenandoahSharedValue)0); + value.release_store_fence((ShenandoahSharedValue)0); } } ShenandoahSharedSemaphore; From f835073f75251c37acce1b5d87e2caf9d748bd75 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Wed, 11 Feb 2026 01:52:55 +0000 Subject: [PATCH 206/215] 8377626: The macOS build is broken after JDK-8332189 Reviewed-by: dholmes --- src/hotspot/share/runtime/javaThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index a891e333d4c..77a567d4a09 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -514,7 +514,7 @@ JavaThread::JavaThread(MemTag mem_tag) : #ifdef MACOS_AARCH64 _cur_wx_enable(nullptr), - _cur_wx_mode(0), + _cur_wx_mode(nullptr), #endif _lock_stack(this), From e516800b3e78e21d68460827ddced9225c3a2247 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 11 Feb 2026 02:02:10 +0000 Subject: [PATCH 207/215] 8219203: Use StringBuilder instead of StringBuffer in com.sun.jndi.dns.ResourceRecord Reviewed-by: alanb, aefimov, rriggs, bpb, lancea --- .../classes/com/sun/jndi/dns/ResourceRecord.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/ResourceRecord.java b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/ResourceRecord.java index e0b8dce556e..a2b927cdaa0 100644 --- a/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/ResourceRecord.java +++ b/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/ResourceRecord.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, 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 @@ -38,7 +38,6 @@ import java.nio.charset.StandardCharsets; * The string format is based on the master file representation in * RFC 1035. * - * @author Scott Seligman */ @@ -484,11 +483,11 @@ public class ResourceRecord { pos += 2; int preference = getUShort(pos); pos += 2; - StringBuffer flags = new StringBuffer(); + StringBuilder flags = new StringBuilder(); pos += decodeCharString(pos, flags); - StringBuffer services = new StringBuffer(); + StringBuilder services = new StringBuilder(); pos += decodeCharString(pos, services); - StringBuffer regexp = new StringBuffer(rdlen); + StringBuilder regexp = new StringBuilder(rdlen); pos += decodeCharString(pos, regexp); DnsName replacement = decodeName(pos); @@ -501,7 +500,7 @@ public class ResourceRecord { * The rdata consists of one or more s. */ private String decodeTxt(int pos) { - StringBuffer buf = new StringBuffer(rdlen); + StringBuilder buf = new StringBuilder(rdlen); int end = pos + rdlen; while (pos < end) { pos += decodeCharString(pos, buf); @@ -517,7 +516,7 @@ public class ResourceRecord { * The rdata consists of two s. */ private String decodeHinfo(int pos) { - StringBuffer buf = new StringBuffer(rdlen); + StringBuilder buf = new StringBuilder(rdlen); pos += decodeCharString(pos, buf); buf.append(' '); pos += decodeCharString(pos, buf); @@ -532,7 +531,7 @@ public class ResourceRecord { * Returns the size of the encoded string, including the initial * length octet. */ - private int decodeCharString(int pos, StringBuffer buf) { + private int decodeCharString(int pos, StringBuilder buf) { int start = buf.length(); // starting index of this string int len = getUByte(pos++); // encoded string length boolean quoted = (len == 0); // quote string if empty From 3a588e5bede4752f320f4b17f6086c9927616959 Mon Sep 17 00:00:00 2001 From: Jaikiran Pai Date: Wed, 11 Feb 2026 02:11:56 +0000 Subject: [PATCH 208/215] 8377338: URLJarFile$URLJarFileEntry need not clone the arrays returned by getCertificates() and getCodeSigners() Reviewed-by: mullan --- .../sun/net/www/protocol/jar/URLJarFile.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java b/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java index d30d18df9d6..45738550f59 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java +++ b/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.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 @@ -34,8 +34,6 @@ import java.util.*; import java.util.jar.*; import java.util.zip.ZipFile; import java.util.zip.ZipEntry; -import java.security.CodeSigner; -import java.security.cert.Certificate; import sun.net.www.ParseUtil; /* URL jar file is a common JarFile subtype used for JarURLConnection */ @@ -165,13 +163,12 @@ public class URLJarFile extends JarFile { } private class URLJarFileEntry extends JarEntry { - private final JarEntry je; URLJarFileEntry(JarEntry je) { super(je); - this.je = je; } + @Override public Attributes getAttributes() throws IOException { if (URLJarFile.this.isSuperMan()) { Map e = URLJarFile.this.superEntries; @@ -183,16 +180,6 @@ public class URLJarFile extends JarFile { } return null; } - - public java.security.cert.Certificate[] getCertificates() { - Certificate[] certs = je.getCertificates(); - return certs == null? null: certs.clone(); - } - - public CodeSigner[] getCodeSigners() { - CodeSigner[] csg = je.getCodeSigners(); - return csg == null? null: csg.clone(); - } } public interface URLJarFileCloseController { From a87da5173f14d503664067713ab229e2e4fb6108 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Wed, 11 Feb 2026 02:29:39 +0000 Subject: [PATCH 209/215] 8377514: jpackage: support passing multiple exceptions to the top-level error handler Reviewed-by: almatvee --- .../AppImageSigningConfigBuilder.java | 28 +- .../internal/MacApplicationBuilder.java | 2 +- .../jdk/jpackage/internal/MacFromOptions.java | 103 +++-- .../internal/MacPkgPackageBuilder.java | 4 +- .../internal/SigningIdentityBuilder.java | 36 +- .../resources/MacResources.properties | 2 +- .../jdk/jpackage/internal/cli/Main.java | 27 +- .../internal/util/function/ExceptionBox.java | 39 +- .../jdk/jpackage/internal/cli/MainTest.java | 359 ++++++++++++------ .../util/function/ExceptionBoxTest.java | 91 ++++- .../tools/jpackage/macosx/MacSignTest.java | 259 +++++++++---- 11 files changed, 657 insertions(+), 293 deletions(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigningConfigBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigningConfigBuilder.java index 6fc7fe004c2..962c821c20f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigningConfigBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/AppImageSigningConfigBuilder.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 @@ -61,24 +61,26 @@ final class AppImageSigningConfigBuilder { return this; } - Optional create() { - return signingIdentityBuilder.create().map(cfg -> { - final var validatedEntitlements = validatedEntitlements(); - return new AppImageSigningConfig.Stub( - Objects.requireNonNull(cfg.identity()), - Objects.requireNonNull(signingIdentifierPrefix), - validatedEntitlements, - cfg.keychain().map(Keychain::name), - Optional.ofNullable(entitlementsResourceName).orElse("entitlements.plist") - ); - }); + AppImageSigningConfig create() { + + var cfg = signingIdentityBuilder.create(); + + var validatedEntitlements = validatedEntitlements(); + + return new AppImageSigningConfig.Stub( + Objects.requireNonNull(cfg.identity()), + Objects.requireNonNull(signingIdentifierPrefix), + validatedEntitlements, + cfg.keychain().map(Keychain::name), + Optional.ofNullable(entitlementsResourceName).orElse("entitlements.plist") + ); } private Optional validatedEntitlements() { return Optional.ofNullable(entitlements); } - private SigningIdentityBuilder signingIdentityBuilder; + private final SigningIdentityBuilder signingIdentityBuilder; private Path entitlements; private String entitlementsResourceName; private String signingIdentifierPrefix; diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java index 2023b425da9..a73a6152f6d 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacApplicationBuilder.java @@ -191,7 +191,7 @@ final class MacApplicationBuilder { } private Optional createSigningConfig() { - return Optional.ofNullable(signingBuilder).flatMap(AppImageSigningConfigBuilder::create); + return Optional.ofNullable(signingBuilder).map(AppImageSigningConfigBuilder::create); } private String validatedBundleName() { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java index f3c1765210f..0598e6bc2a9 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -57,7 +57,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import jdk.jpackage.internal.ApplicationBuilder.MainLauncherStartupInfo; -import jdk.jpackage.internal.SigningIdentityBuilder.ExpiredCertificateException; +import jdk.jpackage.internal.SigningIdentityBuilder.SigningConfigException; import jdk.jpackage.internal.SigningIdentityBuilder.StandardCertificateSelector; import jdk.jpackage.internal.cli.OptionValue; import jdk.jpackage.internal.cli.Options; @@ -75,8 +75,8 @@ import jdk.jpackage.internal.model.MacPkgPackage; import jdk.jpackage.internal.model.PackageType; import jdk.jpackage.internal.model.RuntimeLayout; import jdk.jpackage.internal.util.MacBundle; -import jdk.jpackage.internal.util.Result; import jdk.jpackage.internal.util.RootedPath; +import jdk.jpackage.internal.util.Slot; import jdk.jpackage.internal.util.function.ExceptionBox; @@ -118,21 +118,9 @@ final class MacFromOptions { final boolean sign = MAC_SIGN.findIn(options).orElse(false); final boolean appStore = MAC_APP_STORE.findIn(options).orElse(false); - final var appResult = Result.of(() -> createMacApplicationInternal(options)); + final Optional pkgSigningIdentityBuilder; - final Optional pkgBuilder; - if (appResult.hasValue()) { - final var superPkgBuilder = createMacPackageBuilder(options, appResult.orElseThrow(), MAC_PKG); - pkgBuilder = Optional.of(new MacPkgPackageBuilder(superPkgBuilder)); - } else { - // Failed to create an app. Is it because of the expired certificate? - rethrowIfNotExpiredCertificateException(appResult); - // Yes, the certificate for signing the app image has expired. - // Keep going, try to create a signing config for the package. - pkgBuilder = Optional.empty(); - } - - if (sign) { + if (sign && (MAC_INSTALLER_SIGN_IDENTITY.containsIn(options) || MAC_SIGNING_KEY_NAME.containsIn(options))) { final var signingIdentityBuilder = createSigningIdentityBuilder(options); MAC_INSTALLER_SIGN_IDENTITY.ifPresentIn(options, signingIdentityBuilder::signingIdentity); MAC_SIGNING_KEY_NAME.findIn(options).ifPresent(userName -> { @@ -146,34 +134,43 @@ final class MacFromOptions { signingIdentityBuilder.certificateSelector(StandardCertificateSelector.create(userName, domain)); }); - if (pkgBuilder.isPresent()) { - pkgBuilder.orElseThrow().signingBuilder(signingIdentityBuilder); - } else { - // - // The certificate for signing the app image has expired. Can not create a - // package because there is no app. - // Try to create a signing config for the package and see if the certificate for - // signing the package is also expired. - // - - final var expiredAppCertException = appResult.firstError().orElseThrow(); - - final var pkgSignConfigResult = Result.of(signingIdentityBuilder::create); - try { - rethrowIfNotExpiredCertificateException(pkgSignConfigResult); - // The certificate for the package signing config is also expired! - } catch (RuntimeException ex) { - // Some error occurred trying to configure the signing config for the package. - // Ignore it, bail out with the first error. - throw toUnchecked(expiredAppCertException); - } - - Log.error(pkgSignConfigResult.firstError().orElseThrow().getMessage()); - throw toUnchecked(expiredAppCertException); - } + pkgSigningIdentityBuilder = Optional.of(signingIdentityBuilder); + } else { + pkgSigningIdentityBuilder = Optional.empty(); } - return pkgBuilder.orElseThrow().create(); + ApplicationWithDetails app = null; + try { + app = createMacApplicationInternal(options); + } catch (RuntimeException appEx) { + rethrowIfNotSigningConfigException(appEx); + try { + pkgSigningIdentityBuilder.ifPresent(SigningIdentityBuilder::create); + } catch (RuntimeException pkgEx) { + + if (Objects.equals(appEx.getMessage(), pkgEx.getMessage())) { + // Don't report the same error twice. + throw appEx; + } + + // Use suppressed exceptions to communicate multiple exceptions to the caller. + // The top-level error handling code assumes there is a causal connection + // between the exception and its suppressed exceptions. + // Based on this assumption and following the principle "Present cause before consequence", + // it will report the suppressed exceptions before reporting the caught exception. + pkgEx.addSuppressed(appEx); + throw pkgEx; + } + + throw appEx; + } + + final var superPkgBuilder = createMacPackageBuilder(options, app, MAC_PKG); + final var pkgBuilder = new MacPkgPackageBuilder(superPkgBuilder); + + pkgSigningIdentityBuilder.ifPresent(pkgBuilder::signingBuilder); + + return pkgBuilder.create(); } private record ApplicationWithDetails(MacApplication app, Optional externalApp) { @@ -247,7 +244,7 @@ final class MacFromOptions { appBuilder.appStore(appStore); - if (sign) { + if (sign && (MAC_APP_IMAGE_SIGN_IDENTITY.containsIn(options) || MAC_SIGNING_KEY_NAME.containsIn(options))) { final var signingIdentityBuilder = createSigningIdentityBuilder(options); MAC_APP_IMAGE_SIGN_IDENTITY.ifPresentIn(options, signingIdentityBuilder::signingIdentity); MAC_SIGNING_KEY_NAME.findIn(options).ifPresent(userName -> { @@ -298,20 +295,18 @@ final class MacFromOptions { return builder; } - private static void rethrowIfNotExpiredCertificateException(Result result) { - final var ex = result.firstError().orElseThrow(); + private static void rethrowIfNotSigningConfigException(Exception ex) { + var signingConfigExceptionFound = Slot.createEmpty(); - if (ex instanceof ExpiredCertificateException) { - return; - } - - if (ex instanceof ExceptionBox box) { - if (box.getCause() instanceof Exception cause) { - rethrowIfNotExpiredCertificateException(Result.ofError(cause)); + ExceptionBox.visitUnboxedExceptionsRecursively(ex, visited -> { + if (visited instanceof SigningConfigException) { + signingConfigExceptionFound.set(true); } - } + }); - throw toUnchecked(ex); + if (!signingConfigExceptionFound.find().orElse(false)) { + throw toUnchecked(ex); + } } private static SigningIdentityBuilder createSigningIdentityBuilder(Options options) { diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java index b81340354e1..0a5cc09596f 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacPkgPackageBuilder.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 @@ -48,7 +48,7 @@ final class MacPkgPackageBuilder { } private Optional createSigningConfig() { - return Optional.ofNullable(signingBuilder).flatMap(SigningIdentityBuilder::create).map(cfg -> { + return Optional.ofNullable(signingBuilder).map(SigningIdentityBuilder::create).map(cfg -> { return new PkgSigningConfig.Stub(cfg.identity(), cfg.keychain().map(Keychain::name)); }); } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java index 0f753c07569..892601eb363 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/SigningIdentityBuilder.java @@ -39,23 +39,18 @@ import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.security.auth.x500.X500Principal; import jdk.jpackage.internal.MacCertificateUtils.CertificateHash; -import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.JPackageException; import jdk.jpackage.internal.model.SigningIdentity; final class SigningIdentityBuilder { - static class SigningConfigException extends ConfigException { - SigningConfigException(ConfigException ex) { - super(ex.getMessage(), ex.getAdvice(), ex.getCause()); + static class SigningConfigException extends JPackageException { + public SigningConfigException(String msg) { + super(msg); } - private static final long serialVersionUID = 1L; - } - - static class ExpiredCertificateException extends SigningConfigException { - ExpiredCertificateException(ConfigException ex) { - super(ex); + public SigningConfigException(String msg, Throwable cause) { + super(msg, cause); } private static final long serialVersionUID = 1L; @@ -83,11 +78,12 @@ final class SigningIdentityBuilder { return this; } - Optional create() { - if (signingIdentity == null && certificateSelector == null) { - return Optional.empty(); + SigningConfig create() { + if (Objects.isNull(certificateSelector) == Objects.isNull(signingIdentity)) { + // Either the signing certificate selector or the signing certificate must be configured. + throw new IllegalStateException(); } else { - return Optional.of(new SigningConfig(validatedSigningIdentity(), validatedKeychain())); + return new SigningConfig(validatedSigningIdentity(), validatedKeychain()); } } @@ -133,8 +129,9 @@ final class SigningIdentityBuilder { try { cert.checkValidity(); - } catch (CertificateExpiredException|CertificateNotYetValidException ex) { - throw new ExpiredCertificateException(I18N.buildConfigException("error.certificate.expired", findSubjectCNs(cert).getFirst()).create()); + } catch (CertificateExpiredException | CertificateNotYetValidException ex) { + throw new SigningConfigException(I18N.format( + "error.certificate.outside-validity-period", findSubjectCNs(cert).getFirst()), ex); } final var signingIdentityHash = CertificateHash.of(cert); @@ -148,7 +145,7 @@ final class SigningIdentityBuilder { Objects.requireNonNull(keychain); switch (certs.size()) { case 0 -> { - throw new JPackageException(I18N.format("error.cert.not.found", + throw new SigningConfigException(I18N.format("error.cert.not.found", certificateSelector.signingIdentities().getFirst(), keychain.map(Keychain::name).orElse(""))); } @@ -156,10 +153,9 @@ final class SigningIdentityBuilder { return certs.getFirst(); } default -> { - throw I18N.buildConfigException("error.multiple.certs.found", + throw new SigningConfigException(I18N.format("error.multiple.certs.found", certificateSelector.signingIdentities().getFirst(), - keychain.map(Keychain::name).orElse("") - ).create(); + keychain.map(Keychain::name).orElse(""))); } } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties index e1b154c5933..e43cadc5782 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/resources/MacResources.properties @@ -24,7 +24,7 @@ # # error.invalid-cfbundle-version.advice=Set a compatible 'app-version' value. Valid versions are one to three integers separated by dots. -error.certificate.expired=Certificate expired {0} +error.certificate.outside-validity-period=The certificate "{0}" is outside its validity period error.cert.not.found=No certificate found matching [{0}] using keychain [{1}] error.multiple.certs.found=Multiple certificates matching name [{0}] found in keychain [{1}] error.app-image.mac-sign.required=--mac-sign option is required with predefined application image and with type [app-image] diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java index 73b4850344b..d40895a7da6 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/cli/Main.java @@ -37,6 +37,7 @@ import java.io.PrintWriter; import java.io.StringReader; import java.io.UncheckedIOException; import java.nio.file.NoSuchFileException; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -239,17 +240,21 @@ public final class Main { } void reportError(Throwable t) { - if (t instanceof ConfigException cfgEx) { - printError(cfgEx, Optional.ofNullable(cfgEx.getAdvice())); - } else if (t instanceof ExceptionBox ex) { - reportError(ex.getCause()); - } else if (t instanceof UncheckedIOException ex) { - reportError(ex.getCause()); - } else if (t instanceof UnexpectedResultException ex) { - printExternalCommandError(ex); - } else { - printError(t, Optional.empty()); - } + + var unfoldedExceptions = new ArrayList(); + ExceptionBox.visitUnboxedExceptionsRecursively(t, unfoldedExceptions::add); + + unfoldedExceptions.forEach(ex -> { + if (ex instanceof ConfigException cfgEx) { + printError(cfgEx, Optional.ofNullable(cfgEx.getAdvice())); + } else if (ex instanceof UncheckedIOException) { + printError(ex.getCause(), Optional.empty()); + } else if (ex instanceof UnexpectedResultException urex) { + printExternalCommandError(urex); + } else { + printError(ex, Optional.empty()); + } + }); } private void printExternalCommandError(UnexpectedResultException ex) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ExceptionBox.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ExceptionBox.java index 40503469873..0c2963f7397 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ExceptionBox.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/function/ExceptionBox.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 @@ -25,6 +25,9 @@ package jdk.jpackage.internal.util.function; import java.lang.reflect.InvocationTargetException; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; public class ExceptionBox extends RuntimeException { @@ -74,6 +77,40 @@ public class ExceptionBox extends RuntimeException { } } + /** + * Unboxes the specified throwable and its suppressed throwables recursively. + *

      + * Calls {@link #unbox(Throwable)} on the specified throwable and nested + * suppressed throwables, passing the result to the {@code visitor}. + *

      + * Throwables will be traversed in the "cause before consequence" order. E.g.: + * say exception "A" suppresses exceptions "B" and "C", and "B" suppresses "D". + * The traverse order will be "D", "B", "C", "A". + *

      + * If the method encounters cyclic suppressed throwables, it will fall into an + * infinite recursion loop, eventually causing a {@code StackOverflowError}. + *

      + * If the specified throwable or any of its nested suppressed throwables are of + * type {@link Error}, the method will keep notifying the {@code visitor} until + * it hits the first such throwable. When it happens, the method will throw this + * throwable. + * + * @param t the exception to visit + * @param visitor the callback to apply to every subsequently visited exception + */ + public static void visitUnboxedExceptionsRecursively(Throwable t, Consumer visitor) { + Objects.requireNonNull(t); + Objects.requireNonNull(visitor); + + var ex = unbox(t); + + Stream.of(ex.getSuppressed()).forEach(suppressed -> { + visitUnboxedExceptionsRecursively(suppressed, visitor); + }); + + visitor.accept(ex); + } + public static Error reachedUnreachable() { return new AssertionError("Reached unreachable!"); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java index 46de970a829..239ce0d0ce9 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/cli/MainTest.java @@ -38,11 +38,12 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.spi.ToolProvider; import java.util.stream.Collectors; @@ -53,9 +54,11 @@ import jdk.jpackage.internal.MockUtils; import jdk.jpackage.internal.model.ConfigException; import jdk.jpackage.internal.model.ExecutableAttributesWithCapturedOutput; import jdk.jpackage.internal.model.JPackageException; +import jdk.jpackage.internal.model.SelfContainedException; import jdk.jpackage.internal.util.CommandOutputControl; import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedExitCodeException; import jdk.jpackage.internal.util.CommandOutputControl.UnexpectedResultException; +import jdk.jpackage.internal.util.IdentityWrapper; import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.test.Annotations; import jdk.jpackage.test.JPackageCommand; @@ -181,112 +184,157 @@ public class MainTest extends JUnitAdapter { ).map(TestSpec.Builder::create).toList(); } - private static List test_ErrorReporter() { - var data = new ArrayList(); - + var testCases = new ArrayList(); for (var verbose : List.of(true, false)) { - for (var makeCause : List.>of( - ex -> ex, - // UncheckedIOException - ex -> { - if (ex instanceof IOException ioex) { - return new UncheckedIOException(ioex); - } else { - return null; - } - }, - // ExceptionBox - ex -> { - var rex = ExceptionBox.toUnchecked(ex); - if (rex != ex) { - return rex; - } else { - return null; - } - } - )) { - for (var expect : List.of( - Map.entry(new IOException("I/O error"), true), - Map.entry(new NullPointerException(), true), - Map.entry(new JPackageException("Kaput!"), false), - Map.entry(new ConfigException("It is broken", "Fix it!"), false), - Map.entry(new ConfigException("It is broken. No advice how to fix it", (String)null), false), - Map.entry(new Utils.ParseException("Malformed command line"), false), - Map.entry(new StandardOption.AddLauncherIllegalArgumentException("Malformed value of --add-launcher option"), false) - )) { - var cause = makeCause.apply(expect.getKey()); - if (cause == null) { - continue; - } - - var expectedOutput = new ArrayList(); - if (expect.getValue()) { - // An alien exception. - expectedOutput.add(ExceptionFormatter.STACK_TRACE); - expectedOutput.add(ExceptionFormatter.TO_STRING); - } else { - if (verbose) { - expectedOutput.add(ExceptionFormatter.STACK_TRACE); - } - if (expect.getKey() instanceof ConfigException cex) { - if (cex.getAdvice() != null) { - expectedOutput.add(ExceptionFormatter.MESSAGE_WITH_ADVICE); - } else { - expectedOutput.add(ExceptionFormatter.GET_MESSAGE); - } - } else { - expectedOutput.add(ExceptionFormatter.GET_MESSAGE); - } - } - - data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); - } - } - - var execAttrs = new CommandOutputControl.ProcessAttributes(Optional.of(12345L), List.of("foo", "--bar")); - for (var makeCause : List.>of( - ex -> ex, - ExceptionBox::toUnchecked - )) { - - for (var expect : List.of( - Map.entry( - augmentResultWithOutput( - CommandOutputControl.Result.build().exitCode(135).execAttrs(execAttrs).create(), - "The quick brown fox\njumps over the lazy dog" - ).unexpected("Kaput!"), - ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_OUTPUT_MESSAGE - ), - Map.entry( - new UnexpectedExitCodeException(augmentResultWithOutput( - CommandOutputControl.Result.build().exitCode(135).create(), - "The quick brown fox\njumps" - )), - ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_EXIT_CODE_MESSAGE - ), - Map.entry( - augmentResultWithOutput( - CommandOutputControl.Result.build().create(), - "The quick brown fox\njumps" - ).unexpected("Timed out!"), - ExceptionFormatter.FAILED_COMMAND_TIMEDOUT_MESSAGE - ) - )) { - var cause = makeCause.apply(expect.getKey()); - var expectedOutput = new ArrayList(); - if (verbose) { - expectedOutput.add(ExceptionFormatter.STACK_TRACE); - } - expectedOutput.add(expect.getValue()); - expectedOutput.add(ExceptionFormatter.FAILED_COMMAND_OUTPUT); - data.add(new ErrorReporterTestSpec(cause, expect.getKey(), verbose, expectedOutput)); - } - } - + test_ErrorReporter_Exception(verbose, testCases::add); + test_ErrorReporter_UnexpectedResultException(verbose, testCases::add); + test_ErrorReporter_suppressedExceptions(verbose, testCases::add); } - return data; + return testCases; + } + + private static void test_ErrorReporter_Exception(boolean verbose, Consumer sink) { + + for (var makeCause : List.>of( + ex -> ex, + // UncheckedIOException + ex -> { + if (ex instanceof IOException ioex) { + return new UncheckedIOException(ioex); + } else { + return null; + } + }, + // ExceptionBox + ex -> { + var rex = ExceptionBox.toUnchecked(ex); + if (rex != ex) { + return rex; + } else { + return null; + } + } + )) { + for (var expect : List.of( + new IOException("I/O error"), + new NullPointerException(), + new JPackageException("Kaput!"), + new ConfigException("It is broken", "Fix it!"), + new ConfigException("It is broken. No advice how to fix it", (String)null), + new Utils.ParseException("Malformed command line"), + new StandardOption.AddLauncherIllegalArgumentException("Malformed value of --add-launcher option") + )) { + var cause = makeCause.apply(expect); + if (cause == null) { + continue; + } + + var expectedOutput = new ArrayList(); + ErrorReporterTestSpec.expectExceptionFormatters(expect, verbose, expectedOutput::add); + sink.accept(ErrorReporterTestSpec.create(cause, expect, verbose, expectedOutput)); + } + } + } + + private static void test_ErrorReporter_UnexpectedResultException(boolean verbose, Consumer sink) { + + var execAttrs = new CommandOutputControl.ProcessAttributes(Optional.of(12345L), List.of("foo", "--bar")); + + for (var makeCause : List.>of( + ex -> ex, + ExceptionBox::toUnchecked + )) { + + for (var expect : List.of( + augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(135).execAttrs(execAttrs).create(), + "The quick brown fox\njumps over the lazy dog" + ).unexpected("Kaput!"), + new UnexpectedExitCodeException(augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(135).create(), + "The quick brown fox\njumps" + )), + augmentResultWithOutput( + CommandOutputControl.Result.build().create(), + "The quick brown fox\njumps" + ).unexpected("Timed out!") + )) { + var cause = makeCause.apply(expect); + var expectedOutput = new ArrayList(); + ErrorReporterTestSpec.expectExceptionFormatters(expect, verbose, expectedOutput::add); + sink.accept(ErrorReporterTestSpec.create(cause, expect, verbose, expectedOutput)); + } + } + } + + private static Exception suppressException(Exception main, Exception suppressed) { + Objects.requireNonNull(main); + Objects.requireNonNull(suppressed); + + try (var autoCloseable = new AutoCloseable() { + + @Override + public void close() throws Exception { + throw suppressed; + }}) { + + throw main; + } catch (Exception ex) { + return ex; + } + } + + private static void test_ErrorReporter_suppressedExceptions(boolean verbose, Consumer sink) { + + var execAttrs = new CommandOutputControl.ProcessAttributes(Optional.of(567L), List.of("foo", "--bar")); + + Supplier createUnexpectedResultException = () -> { + return augmentResultWithOutput( + CommandOutputControl.Result.build().exitCode(7).execAttrs(execAttrs).create(), + "The quick brown fox\njumps over the lazy dog" + ).unexpected("Alas"); + }; + + for (var makeCause : List.>of( + ex -> ex, + ex -> { + var rex = ExceptionBox.toUnchecked(ex); + if (rex != ex) { + return rex; + } else { + return null; + } + } + )) { + + for (var exceptions : List.of( + List.of(new JPackageException("Kaput!"), new JPackageException("Suppressed kaput")), + List.of(new Exception("Kaput!"), new JPackageException("Suppressed kaput")), + List.of(new Exception("Kaput!"), new Exception("Suppressed kaput")), + List.of(new Exception("Kaput!"), ExceptionBox.toUnchecked(new Exception("Suppressed kaput"))), + List.of(createUnexpectedResultException.get(), new Exception("Suppressed kaput")), + List.of(new Exception("Alas!"), createUnexpectedResultException.get()), + List.of(new JPackageException("Alas!"), createUnexpectedResultException.get()) + )) { + var main = exceptions.getFirst(); + var suppressed = exceptions.getLast(); + + var cause = makeCause.apply(suppressException(main, suppressed)); + + if (cause == null) { + continue; + } + + var expectedOutput = new ArrayList(); + + ErrorReporterTestSpec.expectOutputFragments(ExceptionBox.unbox(suppressed), verbose, expectedOutput::add); + ErrorReporterTestSpec.expectOutputFragments(main, verbose, expectedOutput::add); + + sink.accept(new ErrorReporterTestSpec(cause, verbose, expectedOutput)); + } + } } @@ -452,6 +500,19 @@ public class MainTest extends JUnitAdapter { } + private record FormattedException(ExceptionFormatter formatter, Exception exception) { + + FormattedException { + Objects.requireNonNull(formatter); + Objects.requireNonNull(exception); + } + + String format() { + return formatter.format(exception); + } + } + + private enum ExceptionFormatter { GET_MESSAGE(errorMessage(Exception::getMessage)), MESSAGE_WITH_ADVICE(ex -> { @@ -497,6 +558,10 @@ public class MainTest extends JUnitAdapter { return formatter.apply(v); } + FormattedException bind(Exception v) { + return new FormattedException(this, v); + } + private static Function errorMessage(Function formatter) { Objects.requireNonNull(formatter); return ex -> { @@ -545,29 +610,99 @@ public class MainTest extends JUnitAdapter { } - record ErrorReporterTestSpec(Exception cause, Exception expect, boolean verbose, List expectOutput) { + record ErrorReporterTestSpec(Exception cause, boolean verbose, List expectOutput) { ErrorReporterTestSpec { Objects.requireNonNull(cause); - Objects.requireNonNull(expect); Objects.requireNonNull(expectOutput); + if (expectOutput.isEmpty()) { + throw new IllegalArgumentException(); + } } - ErrorReporterTestSpec(Exception cause, boolean verbose, List expectOutput) { - this(cause, cause, verbose, expectOutput); + static ErrorReporterTestSpec create( + Exception cause, boolean verbose, List expectOutput) { + return create(cause, cause, verbose, expectOutput); + } + + static ErrorReporterTestSpec create( + Exception cause, Exception expect, boolean verbose, List expectOutput) { + Objects.requireNonNull(cause); + Objects.requireNonNull(expect); + return new ErrorReporterTestSpec(cause, verbose, expectOutput.stream().map(formatter -> { + return new FormattedException(formatter, expect); + }).toList()); + } + + static void expectExceptionFormatters(Exception ex, boolean verbose, Consumer sink) { + Objects.requireNonNull(ex); + Objects.requireNonNull(sink); + + final var isSelfContained = (ex.getClass().getAnnotation(SelfContainedException.class) != null); + + if (verbose || !(isSelfContained || ex instanceof UnexpectedResultException)) { + sink.accept(ExceptionFormatter.STACK_TRACE); + } + + switch (ex) { + case ConfigException cex -> { + if (cex.getAdvice() != null) { + sink.accept(ExceptionFormatter.MESSAGE_WITH_ADVICE); + } else { + sink.accept(ExceptionFormatter.GET_MESSAGE); + } + } + case UnexpectedResultException urex -> { + if (urex instanceof UnexpectedExitCodeException) { + sink.accept(ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_EXIT_CODE_MESSAGE); + } else if (urex.getResult().exitCode().isPresent()) { + sink.accept(ExceptionFormatter.FAILED_COMMAND_UNEXPECTED_OUTPUT_MESSAGE); + } else { + sink.accept(ExceptionFormatter.FAILED_COMMAND_TIMEDOUT_MESSAGE); + } + sink.accept(ExceptionFormatter.FAILED_COMMAND_OUTPUT); + } + default -> { + if (isSelfContained) { + sink.accept(ExceptionFormatter.GET_MESSAGE); + } else { + sink.accept(ExceptionFormatter.TO_STRING); + } + } + } + } + + static void expectOutputFragments(Exception ex, boolean verbose, Consumer sink) { + Objects.requireNonNull(sink); + expectExceptionFormatters(ex, verbose, formatter -> { + sink.accept(formatter.bind(ex)); + }); } @Override public String toString() { var tokens = new ArrayList(); - if (cause == expect) { + var expect = expectOutput.stream() + .map(FormattedException::exception) + .map(IdentityWrapper::new) + .distinct() + .toList(); + + if (expect.size() == 1 && expect.getFirst().value() == cause) { tokens.add(cause.toString()); } else { - tokens.add(String.format("[%s] => [%s]", cause, expect)); + tokens.add(String.format("[%s] => %s", cause, expect.stream().map(IdentityWrapper::value).toList())); } - tokens.add(expectOutput.stream().map(Enum::name).collect(Collectors.joining("+"))); + if (expect.size() == 1) { + tokens.add(expectOutput.stream().map(FormattedException::formatter).map(Enum::name).collect(Collectors.joining("+"))); + } else { + tokens.add(expectOutput.stream().map(fragment -> { + var idx = expect.indexOf(IdentityWrapper.wrapIdentity(fragment.exception())); + return String.format("%s@%d", fragment.formatter(), idx); + }).collect(Collectors.joining("+"))); + } if (verbose) { tokens.add("verbose"); @@ -587,9 +722,7 @@ public class MainTest extends JUnitAdapter { }, verbose).reportError(cause); } - var expected = expectOutput.stream().map(formatter -> { - return formatter.format(expect); - }).collect(Collectors.joining("")); + var expected = expectOutput.stream().map(FormattedException::format).collect(Collectors.joining("")); assertEquals(expected, sink.toString()); } diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/function/ExceptionBoxTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/function/ExceptionBoxTest.java index c3ef239b02d..d7a6f0e714d 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/function/ExceptionBoxTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/function/ExceptionBoxTest.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 @@ -31,13 +31,19 @@ import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import java.util.function.UnaryOperator; +import jdk.jpackage.internal.util.IdentityWrapper; import jdk.jpackage.internal.util.Slot; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; public class ExceptionBoxTest { @@ -190,6 +196,16 @@ public class ExceptionBoxTest { } } + @ParameterizedTest + @MethodSource + void test_visitUnboxedExceptionsRecursively(Throwable t, List expect) { + var actual = new ArrayList(); + ExceptionBox.visitUnboxedExceptionsRecursively(t, actual::add); + assertEquals( + expect.stream().map(IdentityWrapper::new).toList(), + actual.stream().map(IdentityWrapper::new).toList()); + } + public enum InvocationTargetExceptionType { CHECKED("throwIOException", t -> { return t.getCause(); @@ -224,13 +240,74 @@ public class ExceptionBoxTest { throw new Error("Kaput!"); } - private static void assertToUnchecked(Exception cause, boolean asis) { - Class expectedType; - if (asis) { - expectedType = cause.getClass(); - } else { - expectedType = ExceptionBox.class; + private static Collection test_visitUnboxedExceptionsRecursively() { + + var testCases = new ArrayList(); + + testCases.addAll(test_visitUnboxedExceptionsRecursively_example()); + + var t = new Exception("A"); + for (var box : List.>of( + ex -> ex, + ExceptionBox::toUnchecked, + InvocationTargetException::new + )) { + testCases.add(Arguments.of(box.apply(t), List.of(t))); } + + // The cause is not traversed. + var exWithCause = new Exception("B", t); + testCases.add(Arguments.of(exWithCause, List.of(exWithCause))); + + var exWithSuppressed = new Exception("C"); + exWithSuppressed.addSuppressed(t); + exWithSuppressed.addSuppressed(ExceptionBox.toUnchecked(t)); + exWithSuppressed.addSuppressed(exWithCause); + exWithSuppressed.addSuppressed(new InvocationTargetException(exWithCause)); + var exWithSuppressedExpect = List.of(t, t, exWithCause, exWithCause, exWithSuppressed); + testCases.add(Arguments.of(exWithSuppressed, exWithSuppressedExpect)); + + for (var box : List.>of( + ExceptionBox::toUnchecked, + InvocationTargetException::new + )) { + // Suppressed exceptions added to ExceptionBox and InvocationTargetException are omitted. + var exWithSuppressedBoxed = box.apply(exWithSuppressed); + exWithSuppressedBoxed.addSuppressed(exWithSuppressed); + testCases.add(Arguments.of(exWithSuppressedBoxed, exWithSuppressedExpect)); + } + + var exWithSuppressed2 = new Exception("D"); + exWithSuppressed2.addSuppressed(t); + exWithSuppressed2.addSuppressed(exWithSuppressed); + exWithSuppressed2.addSuppressed(ExceptionBox.toUnchecked(exWithSuppressed)); + + var exWithSuppressed2Expect = new ArrayList(); + exWithSuppressed2Expect.add(t); + exWithSuppressed2Expect.addAll(exWithSuppressedExpect); + exWithSuppressed2Expect.addAll(exWithSuppressedExpect); + exWithSuppressed2Expect.add(exWithSuppressed2); + testCases.add(Arguments.of(exWithSuppressed2, exWithSuppressed2Expect)); + + return testCases; + } + + private static Collection test_visitUnboxedExceptionsRecursively_example() { + + var a = new Exception("A"); + var b = new Exception("B"); + var c = new Exception("C"); + var d = new Exception("D"); + + a.addSuppressed(b); + a.addSuppressed(c); + + b.addSuppressed(d); + + return List.of(Arguments.of(a, List.of(d, b, c, a))); + } + + private static void assertToUnchecked(Exception cause, boolean asis) { var unchecked = ExceptionBox.toUnchecked(cause); if (asis) { assertSame(cause, unchecked); diff --git a/test/jdk/tools/jpackage/macosx/MacSignTest.java b/test/jdk/tools/jpackage/macosx/MacSignTest.java index 0be494ea469..e558b671478 100644 --- a/test/jdk/tools/jpackage/macosx/MacSignTest.java +++ b/test/jdk/tools/jpackage/macosx/MacSignTest.java @@ -22,22 +22,23 @@ */ import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY; -import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_IDENTITY_APP_IMAGE; import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_USER_FULL_NAME; import static jdk.jpackage.test.MacHelper.SignKeyOption.Type.SIGN_KEY_USER_SHORT_NAME; import java.io.IOException; import java.nio.file.Files; import java.util.Collection; +import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Stream; +import jdk.jpackage.internal.util.function.ExceptionBox; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.FailedCommandErrorValidator; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; @@ -47,7 +48,6 @@ import jdk.jpackage.test.MacHelper.ResolvableCertificateRequest; import jdk.jpackage.test.MacHelper.SignKeyOption; import jdk.jpackage.test.MacHelper.SignKeyOptionWithKeychain; import jdk.jpackage.test.MacSign; -import jdk.jpackage.test.MacSign.CertificateType; import jdk.jpackage.test.MacSignVerify; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; @@ -94,11 +94,7 @@ public class MacSignTest { SigningBase.StandardCertificateRequest.CODESIGN, keychain); - new FailedCommandErrorValidator(Pattern.compile(String.format( - "/usr/bin/codesign -s %s -vvvv --timestamp --options runtime --prefix \\S+ --keychain %s --entitlements \\S+ \\S+", - Pattern.quote(String.format("'%s'", signingKeyOption.certRequest().name())), - Pattern.quote(keychain.name()) - ))).exitCode(1).createGroup().mutate(group::add); + buildSignCommandErrorValidator(signingKeyOption).createGroup().mutate(group::add); MacSign.withKeychain(_ -> { @@ -107,7 +103,7 @@ public class MacSignTest { // This is not a fatal error, just a warning. // To make jpackage fail, specify bad additional content. JPackageCommand.helloAppImage() - .ignoreDefaultVerbose(true) + .mutate(MacSignTest::init) .validateOutput(group.create()) .addArguments("--app-content", appContent) .mutate(signingKeyOption::addTo) @@ -136,11 +132,9 @@ public class MacSignTest { MacSign.withKeychain(keychain -> { // Build a matcher for jpackage's failed command output. - var errorValidator = new FailedCommandErrorValidator(Pattern.compile(String.format( - "/usr/bin/codesign -s %s -vvvv --timestamp --options runtime --prefix \\S+ --keychain %s", - Pattern.quote(String.format("'%s'", signingKeyOption.certRequest().name())), - Pattern.quote(keychain.name()) - ))).exitCode(1).output(String.format("%s: no identity found", signingKeyOption.certRequest().name())).createGroup(); + var errorValidator = buildSignCommandErrorValidator(signingKeyOption, keychain) + .output(String.format("%s: no identity found", signingKeyOption.certRequest().name())) + .createGroup(); JPackageCommand.helloAppImage() .setFakeRuntime() @@ -157,9 +151,12 @@ public class MacSignTest { @Parameter({"IMAGE", "EXPIRED_SIGNING_KEY_USER_NAME"}) @Parameter({"MAC_DMG", "EXPIRED_SIGNING_KEY_USER_NAME"}) @Parameter({"MAC_PKG", "EXPIRED_SIGNING_KEY_USER_NAME", "EXPIRED_SIGNING_KEY_USER_NAME_PKG"}) + @Parameter({"MAC_PKG", "EXPIRED_SIGNING_KEY_USER_NAME_PKG", "EXPIRED_SIGNING_KEY_USER_NAME"}) @Parameter({"IMAGE", "EXPIRED_SIGN_IDENTITY"}) @Parameter({"MAC_DMG", "EXPIRED_SIGN_IDENTITY"}) + + // Test that jpackage doesn't print duplicated error messages. @Parameter({"MAC_PKG", "EXPIRED_SIGN_IDENTITY"}) @Parameter({"IMAGE", "EXPIRED_CODESIGN_SIGN_IDENTITY"}) @@ -168,16 +165,41 @@ public class MacSignTest { @Parameter({"MAC_PKG", "GOOD_CODESIGN_SIGN_IDENTITY", "EXPIRED_PKG_SIGN_IDENTITY"}) @Parameter({"MAC_PKG", "EXPIRED_CODESIGN_SIGN_IDENTITY", "GOOD_PKG_SIGN_IDENTITY"}) - public static void testExpiredCertificate(PackageType type, SignOption... options) { + public static void testExpiredCertificate(PackageType type, SignOption... optionIds) { + + var options = Stream.of(optionIds).map(SignOption::option).toList(); + + var badOptions = options.stream().filter(option -> { + return option.certRequest().expired(); + }).toList(); MacSign.withKeychain(keychain -> { + final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain) - .ignoreDefaultVerbose(true) - .addArguments(Stream.of(options).map(SignOption::args).flatMap(List::stream).toList()) + .mutate(MacSignTest::init) + .addArguments(options.stream().map(SignKeyOption::asCmdlineArgs).flatMap(List::stream).toList()) .setPackageType(type); - SignOption.configureOutputValidation(cmd, Stream.of(options).filter(SignOption::expired).toList(), opt -> { - return JPackageStringBundle.MAIN.cannedFormattedString("error.certificate.expired", opt.identityName()); + configureOutputValidation(cmd, expectFailures(badOptions), opt -> { + if (!opt.passThrough().orElse(false)) { + return expectConfigurationError("error.certificate.outside-validity-period", opt.certRequest().name()); + } else { + var builder = buildSignCommandErrorValidator(opt, keychain); + switch (opt.optionName().orElseThrow()) { + case KEY_IDENTITY_APP_IMAGE, KEY_USER_NAME -> { + builder.output(String.format("%s: no identity found", opt.certRequest().name())); + } + case KEY_IDENTITY_INSTALLER -> { + var regexp = Pattern.compile(String.format( + "^productbuild: error: Cannot write product to \\S+ \\(Could not find appropriate signing identity for “%s” in keychain at “%s”\\.\\)", + Pattern.quote(opt.certRequest().name()), + Pattern.quote(keychain.name()) + )); + builder.validators(TKit.assertTextStream(regexp)); + } + } + return builder.createGroup(); + } }).execute(1); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.EXPIRED.keychain()); } @@ -185,30 +207,60 @@ public class MacSignTest { @Test @Parameter({"IMAGE", "GOOD_SIGNING_KEY_USER_NAME"}) @Parameter({"MAC_DMG", "GOOD_SIGNING_KEY_USER_NAME"}) + @Parameter({"MAC_PKG", "GOOD_SIGNING_KEY_USER_NAME", "GOOD_SIGNING_KEY_USER_NAME_PKG"}) @Parameter({"MAC_PKG", "GOOD_SIGNING_KEY_USER_NAME_PKG", "GOOD_SIGNING_KEY_USER_NAME"}) @Parameter({"IMAGE", "GOOD_CODESIGN_SIGN_IDENTITY"}) @Parameter({"MAC_PKG", "GOOD_CODESIGN_SIGN_IDENTITY", "GOOD_PKG_SIGN_IDENTITY"}) @Parameter({"MAC_PKG", "GOOD_PKG_SIGN_IDENTITY"}) - public static void testMultipleCertificates(PackageType type, SignOption... options) { + public static void testMultipleCertificates(PackageType type, SignOption... optionIds) { + + var options = Stream.of(optionIds).map(SignOption::option).toList(); MacSign.withKeychain(keychain -> { + final var cmd = MacHelper.useKeychain(JPackageCommand.helloAppImage(), keychain) - .ignoreDefaultVerbose(true) - .addArguments(Stream.of(options).map(SignOption::args).flatMap(List::stream).toList()) + .mutate(MacSignTest::init) + .addArguments(options.stream().map(SignKeyOption::asCmdlineArgs).flatMap(List::stream).toList()) .setPackageType(type); - Predicate filter = opt -> { - if (type == PackageType.MAC_PKG && options.length > 1) { - // Only the first error will be reported and it should always be - // for the app image signing, not for the PKG signing. - return opt.identityType() == CertificateType.CODE_SIGN; - } else { - return true; - } - }; + if (List.of(optionIds).equals(List.of(SignOption.GOOD_PKG_SIGN_IDENTITY))) { + /** + * Normally, if multiple signing identities share the same name, signing should + * fail. However, in pass-through signing, when jpackage passes signing + * identities without validation to signing commands, signing a .pkg installer + * with a name matching two signing identities succeeds. + */ + var group = TKit.TextStreamVerifier.group(); - SignOption.configureOutputValidation(cmd, Stream.of(options).filter(filter).toList(), opt -> { - return JPackageStringBundle.MAIN.cannedFormattedString("error.multiple.certs.found", opt.identityName(), keychain.name()); + group.add(TKit.assertTextStream(JPackageStringBundle.MAIN.cannedFormattedString( + "warning.unsigned.app.image", "pkg").getValue())); + + cmd.validateOutput(group.create().andThen(TKit.assertEndOfTextStream())); + + cmd.execute(); + return; + } + + configureOutputValidation(cmd, expectFailures(options), opt -> { + if (!opt.passThrough().orElse(false)) { + return expectConfigurationError("error.multiple.certs.found", opt.certRequest().name(), keychain.name()); + } else { + var builder = buildSignCommandErrorValidator(opt, keychain); + switch (opt.optionName().orElseThrow()) { + case KEY_IDENTITY_APP_IMAGE, KEY_USER_NAME -> { + builder.output(String.format("%s: ambiguous", opt.certRequest().name())); + } + case KEY_IDENTITY_INSTALLER -> { + var regexp = Pattern.compile(String.format( + "^productbuild: error: Cannot write product to \\S+ \\(Could not find appropriate signing identity for “%s” in keychain at “%s”\\.\\)", + Pattern.quote(opt.certRequest().name()), + Pattern.quote(keychain.name()) + )); + builder.validators(TKit.assertTextStream(regexp)); + } + } + return builder.createGroup(); + } }).execute(1); }, MacSign.Keychain.UsageBuilder::addToSearchList, SigningBase.StandardKeychain.DUPLICATE.keychain()); } @@ -262,7 +314,7 @@ public class MacSignTest { GOOD_SIGNING_KEY_USER_NAME(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.CODESIGN), GOOD_SIGNING_KEY_USER_NAME_PKG(SIGN_KEY_USER_SHORT_NAME, SigningBase.StandardCertificateRequest.PKG), GOOD_CODESIGN_SIGN_IDENTITY(SIGN_KEY_IDENTITY, SigningBase.StandardCertificateRequest.CODESIGN), - GOOD_PKG_SIGN_IDENTITY(SIGN_KEY_IDENTITY_APP_IMAGE, SigningBase.StandardCertificateRequest.PKG); + GOOD_PKG_SIGN_IDENTITY(SIGN_KEY_IDENTITY, SigningBase.StandardCertificateRequest.PKG); SignOption(SignKeyOption.Type optionType, NamedCertificateRequestSupplier certRequestSupplier) { this.option = new SignKeyOption(optionType, new ResolvableCertificateRequest(certRequestSupplier.certRequest(), _ -> { @@ -270,44 +322,111 @@ public class MacSignTest { }, certRequestSupplier.name())); } - boolean passThrough() { - return option.type().mapOptionName(option.certRequest().type()).orElseThrow().passThrough(); - } - - boolean expired() { - return option.certRequest().expired(); - } - - String identityName() { - return option.certRequest().name(); - } - - CertificateType identityType() { - return option.certRequest().type(); - } - - List args() { - return option.asCmdlineArgs(); - } - - static JPackageCommand configureOutputValidation(JPackageCommand cmd, List options, - Function conv) { - options.stream().filter(SignOption::passThrough) - .map(conv) - .map(CannedFormattedString::getValue) - .map(TKit::assertTextStream) - .map(TKit.TextStreamVerifier::negate) - .forEach(cmd::validateOutput); - - options.stream().filter(Predicate.not(SignOption::passThrough)) - .map(conv) - .map(CannedFormattedString::getValue) - .map(TKit::assertTextStream) - .forEach(cmd::validateOutput); - - return cmd; + SignKeyOption option() { + return option; } private final SignKeyOption option; } + + private static JPackageCommand configureOutputValidation(JPackageCommand cmd, Stream options, + Function conv) { + + // Validate jpackage's output matches expected output. + + var outputValidator = options.sorted(Comparator.comparing(option -> { + return option.certRequest().type(); + })).map(conv).reduce( + TKit.TextStreamVerifier.group(), + TKit.TextStreamVerifier.Group::add, + TKit.TextStreamVerifier.Group::add).tryCreate().map(consumer -> { + return consumer.andThen(TKit.assertEndOfTextStream()); + }).orElseGet(TKit::assertEndOfTextStream); + + cmd.validateOutput(outputValidator); + + return cmd; + } + + private static Stream expectFailures(Collection options) { + + if (!options.stream().map(option -> { + return option.passThrough().orElse(false); + }).distinct().reduce((x, y) -> { + throw new IllegalArgumentException(); + }).get()) { + // No pass-through signing options. + // This means jpackage will validate them at the configuration phase and report all detected errors. + return options.stream(); + } + + // Pass-through signing options. + // jpackage will not validate them and will report only one error at the packaging phase. + + Function> filterType = type -> { + return option -> { + return option.certRequest().type() == type; + }; + }; + + var appImageSignOption = options.stream() + .filter(filterType.apply(MacSign.CertificateType.CODE_SIGN)).findFirst(); + var pkgSignOption = options.stream() + .filter(filterType.apply(MacSign.CertificateType.INSTALLER)).findFirst(); + + if (appImageSignOption.isPresent() && pkgSignOption.isPresent()) { + return options.stream().filter(option -> { + return appImageSignOption.get() == option; + }); + } else { + return options.stream(); + } + } + + private static TKit.TextStreamVerifier.Group expectConfigurationError(String key, Object ... args) { + var str = JPackageStringBundle.MAIN.cannedFormattedString(key, args); + str = JPackageCommand.makeError(str); + return TKit.TextStreamVerifier.group().add(TKit.assertTextStream(str.getValue()).predicate(String::equals)); + } + + private static FailedCommandErrorValidator buildSignCommandErrorValidator(SignKeyOptionWithKeychain option) { + return buildSignCommandErrorValidator(option.signKeyOption(), option.keychain()); + } + + private static FailedCommandErrorValidator buildSignCommandErrorValidator(SignKeyOption option, MacSign.ResolvedKeychain keychain) { + + Objects.requireNonNull(option); + Objects.requireNonNull(keychain); + + String cmdlinePatternFormat; + String signIdentity; + + switch (option.optionName().orElseThrow()) { + case KEY_IDENTITY_APP_IMAGE, KEY_USER_NAME -> { + cmdlinePatternFormat = "^/usr/bin/codesign -s %s -vvvv --timestamp --options runtime --prefix \\S+ --keychain %s"; + if (option.passThrough().orElse(false)) { + signIdentity = String.format("'%s'", option.asCmdlineArgs().getLast()); + } else { + signIdentity = MacSign.CertificateHash.of(option.certRequest().cert()).toString(); + } + } + case KEY_IDENTITY_INSTALLER -> { + cmdlinePatternFormat = "^/usr/bin/productbuild --resources \\S+ --sign %s --keychain %s"; + signIdentity = String.format("'%s'", option.asCmdlineArgs().getLast()); + } + default -> { + throw ExceptionBox.reachedUnreachable(); + } + } + + return new FailedCommandErrorValidator(Pattern.compile(String.format( + cmdlinePatternFormat, + Pattern.quote(signIdentity), + Pattern.quote(keychain.name()) + ))).exitCode(1); + } + + private static void init(JPackageCommand cmd) { + cmd.setFakeRuntime().ignoreDefaultVerbose(true); + } } From 56afb460a0055206a1deb1260f6440de9d437acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Maillard?= Date: Wed, 11 Feb 2026 08:27:48 +0000 Subject: [PATCH 210/215] 8375038: C2: Enforce that Ideal() returns the root of the subgraph if any change was made by checking the node hash Reviewed-by: qamai, mchevalier --- src/hotspot/share/opto/c2_globals.hpp | 6 ++++-- src/hotspot/share/opto/cfgnode.cpp | 9 +++++---- src/hotspot/share/opto/intrinsicnode.cpp | 6 +++--- src/hotspot/share/opto/node.cpp | 12 ++++++++---- src/hotspot/share/opto/phaseX.cpp | 14 +++++++++++++- src/hotspot/share/opto/phaseX.hpp | 6 +++++- .../runtime/flags/jvmFlagConstraintsCompiler.cpp | 4 ++-- .../jtreg/compiler/c2/TestVerifyIterativeGVN.java | 4 ++-- 8 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index f470049bf7e..1662f808286 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -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 @@ -694,7 +694,9 @@ "Print progress during Iterative Global Value Numbering") \ \ develop(uint, VerifyIterativeGVN, 0, \ - "Verify Iterative Global Value Numbering =EDCBA, with:" \ + "Verify Iterative Global Value Numbering =FEDCBA, with:" \ + " F: verify Node::Ideal does not return nullptr if the node" \ + "hash has changed" \ " E: verify node specific invariants" \ " D: verify Node::Identity did not miss opportunities" \ " C: verify Node::Ideal did not miss opportunities" \ diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index efa0b55e2c2..c65bc391792 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -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 @@ -2243,7 +2243,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { PhaseIterGVN* igvn = phase->is_IterGVN(); if (wait_for_cast_input_igvn(igvn)) { igvn->_worklist.push(this); - return nullptr; + return progress; } uncasted = true; uin = unique_input(phase, true); @@ -2320,6 +2320,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { PhaseIterGVN* igvn = phase->is_IterGVN(); for (uint i = 1; i < req(); i++) { set_req_X(i, cast, igvn); + progress = this; } uin = cast; } @@ -2338,7 +2339,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); - return nullptr; + return progress; } Node* opt = nullptr; @@ -2529,7 +2530,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Phi references itself through all other inputs then splitting the // Phi through memory merges would create dead loop at later stage. if (ii == top) { - return nullptr; // Delay optimization until graph is cleaned. + return progress; // Delay optimization until graph is cleaned. } if (ii->is_MergeMem()) { MergeMemNode* n = ii->as_MergeMem(); diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 1397d31af53..c3e003ad8d3 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.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 @@ -46,8 +46,8 @@ Node* StrIntrinsicNode::Ideal(PhaseGVN* phase, bool can_reshape) { if (in(0) && in(0)->is_top()) return nullptr; if (can_reshape) { - Node* mem = phase->transform(in(MemNode::Memory)); - // If transformed to a MergeMem, get the desired slice + Node* mem = in(MemNode::Memory); + // If mem input is a MergeMem, get the desired slice uint alias_idx = phase->C->get_alias_index(adr_type()); mem = mem->is_MergeMem() ? mem->as_MergeMem()->memory_at(alias_idx) : mem; if (mem != in(MemNode::Memory)) { diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 832727a424e..cb5795a1250 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -1151,15 +1151,19 @@ const Type* Node::Value(PhaseGVN* phase) const { // 'Idealize' the graph rooted at this Node. // // In order to be efficient and flexible there are some subtle invariants -// these Ideal calls need to hold. Running with '-XX:VerifyIterativeGVN=1' checks -// these invariants, although its too slow to have on by default. If you are -// hacking an Ideal call, be sure to test with '-XX:VerifyIterativeGVN=1' +// these Ideal calls need to hold. Some of the flag bits for '-XX:VerifyIterativeGVN' +// can help with validating these invariants, although they are too slow to have on by default: +// - '-XX:VerifyIterativeGVN=1' checks the def-use info +// - '-XX:VerifyIterativeGVN=100000' checks the return value +// If you are hacking an Ideal call, be sure to use these. // // The Ideal call almost arbitrarily reshape the graph rooted at the 'this' // pointer. If ANY change is made, it must return the root of the reshaped // graph - even if the root is the same Node. Example: swapping the inputs // to an AddINode gives the same answer and same root, but you still have to -// return the 'this' pointer instead of null. +// return the 'this' pointer instead of null. If the node was already dead +// before the Ideal call, this rule does not apply, and it is fine to return +// nullptr even if modifications were made. // // You cannot return an OLD Node, except for the 'this' pointer. Use the // Identity call to return an old Node; basically if Identity can find diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index ce24c46590d..d2b2904b545 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -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 @@ -2167,9 +2167,15 @@ Node *PhaseIterGVN::transform_old(Node* n) { DEBUG_ONLY(dead_loop_check(k);) DEBUG_ONLY(bool is_new = (k->outcnt() == 0);) C->remove_modified_node(k); +#ifndef PRODUCT + uint hash_before = is_verify_Ideal_return() ? k->hash() : 0; +#endif Node* i = apply_ideal(k, /*can_reshape=*/true); assert(i != k || is_new || i->outcnt() > 0, "don't return dead nodes"); #ifndef PRODUCT + if (is_verify_Ideal_return()) { + assert(k->outcnt() == 0 || i != nullptr || hash_before == k->hash(), "hash changed after Ideal returned nullptr for %s", k->Name()); + } verify_step(k); #endif @@ -2193,9 +2199,15 @@ Node *PhaseIterGVN::transform_old(Node* n) { // Try idealizing again DEBUG_ONLY(is_new = (k->outcnt() == 0);) C->remove_modified_node(k); +#ifndef PRODUCT + uint hash_before = is_verify_Ideal_return() ? k->hash() : 0; +#endif i = apply_ideal(k, /*can_reshape=*/true); assert(i != k || is_new || (i->outcnt() > 0), "don't return dead nodes"); #ifndef PRODUCT + if (is_verify_Ideal_return()) { + assert(k->outcnt() == 0 || i != nullptr || hash_before == k->hash(), "hash changed after Ideal returned nullptr for %s", k->Name()); + } verify_step(k); #endif DEBUG_ONLY(loop_count++;) diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index ce02f456c00..cc0d47bd634 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.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 @@ -627,6 +627,10 @@ public: // '-XX:VerifyIterativeGVN=10000' return ((VerifyIterativeGVN % 100000) / 10000) == 1; } + static bool is_verify_Ideal_return() { + // '-XX:VerifyIterativeGVN=100000' + return ((VerifyIterativeGVN % 1000000) / 100000) == 1; + } protected: // Sub-quadratic implementation of '-XX:VerifyIterativeGVN=1' (Use-Def verification). julong _verify_counter; diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp index 63d3424b3ed..47a02184d29 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.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 @@ -306,7 +306,7 @@ JVMFlag::Error TypeProfileLevelConstraintFunc(uint value, bool verbose) { } JVMFlag::Error VerifyIterativeGVNConstraintFunc(uint value, bool verbose) { - const int max_modes = 5; + const int max_modes = 6; uint original_value = value; for (int i = 0; i < max_modes; i++) { if (value % 10 > 1) { diff --git a/test/hotspot/jtreg/compiler/c2/TestVerifyIterativeGVN.java b/test/hotspot/jtreg/compiler/c2/TestVerifyIterativeGVN.java index 4b6215b25bd..39bb8b0a0ad 100644 --- a/test/hotspot/jtreg/compiler/c2/TestVerifyIterativeGVN.java +++ b/test/hotspot/jtreg/compiler/c2/TestVerifyIterativeGVN.java @@ -25,9 +25,9 @@ * @test * @bug 8238756 8351889 * @requires vm.debug == true & vm.flavor == "server" - * @summary Run with -Xcomp to test -XX:VerifyIterativeGVN=11111 in debug builds. + * @summary Run with -Xcomp to test -XX:VerifyIterativeGVN=111111 in debug builds. * - * @run main/othervm/timeout=300 -Xcomp -XX:VerifyIterativeGVN=11111 compiler.c2.TestVerifyIterativeGVN + * @run main/othervm/timeout=300 -Xcomp -XX:VerifyIterativeGVN=111111 compiler.c2.TestVerifyIterativeGVN */ package compiler.c2; From 1e99cc4880f695c12705d849d41609f176f897bd Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 11 Feb 2026 09:14:31 +0000 Subject: [PATCH 211/215] 8376355: Update to use jtreg 8.2.1 Reviewed-by: iris, erikj, shade --- make/autoconf/lib-tests.m4 | 4 ++-- make/conf/github-actions.conf | 4 ++-- make/conf/jib-profiles.js | 6 +++--- test/docs/TEST.ROOT | 4 ++-- test/hotspot/jtreg/TEST.ROOT | 4 ++-- test/jaxp/TEST.ROOT | 2 +- test/jdk/TEST.ROOT | 4 ++-- test/langtools/TEST.ROOT | 2 +- test/lib-test/TEST.ROOT | 4 ++-- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4 index 31d48055984..faaf229eacd 100644 --- a/make/autoconf/lib-tests.m4 +++ b/make/autoconf/lib-tests.m4 @@ -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 @@ -28,7 +28,7 @@ ################################################################################ # Minimum supported versions -JTREG_MINIMUM_VERSION=8.1 +JTREG_MINIMUM_VERSION=8.2.1 GTEST_MINIMUM_VERSION=1.14.0 ################################################################################ diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index bd73e909062..ebfc9191535 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -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 @@ -26,7 +26,7 @@ # Versions and download locations for dependencies used by GitHub Actions (GHA) GTEST_VERSION=1.14.0 -JTREG_VERSION=8.1+1 +JTREG_VERSION=8.2.1+1 LINUX_X64_BOOT_JDK_EXT=tar.gz LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk25/bd75d5f9689641da8e1daabeccb5528b/36/GPL/openjdk-25_linux-x64_bin.tar.gz diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 93aeebc0dd6..76a94b7789e 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -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 @@ -1174,9 +1174,9 @@ var getJibProfilesDependencies = function (input, common) { jtreg: { server: "jpg", product: "jtreg", - version: "8.1", + version: "8.2.1", build_number: "1", - file: "bundles/jtreg-8.1+1.zip", + file: "bundles/jtreg-8.2.1+1.zip", environment_name: "JT_HOME", environment_path: input.get("jtreg", "home_path") + "/bin", configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"), diff --git a/test/docs/TEST.ROOT b/test/docs/TEST.ROOT index 69e66b08b88..9a7e66b631c 100644 --- a/test/docs/TEST.ROOT +++ b/test/docs/TEST.ROOT @@ -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 @@ -38,7 +38,7 @@ groups=TEST.groups # Minimum jtreg version -requiredVersion=8.1+1 +requiredVersion=8.2.1+1 # Use new module options useNewOptions=true diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 0fdbdae9bc5..d1c72b9768c 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -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 @@ -107,7 +107,7 @@ requires.properties= \ jdk.static # Minimum jtreg version -requiredVersion=8.1+1 +requiredVersion=8.2.1+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../../ notation to reach them diff --git a/test/jaxp/TEST.ROOT b/test/jaxp/TEST.ROOT index 82504c251c0..aff3b769830 100644 --- a/test/jaxp/TEST.ROOT +++ b/test/jaxp/TEST.ROOT @@ -23,7 +23,7 @@ modules=java.xml groups=TEST.groups # Minimum jtreg version -requiredVersion=8.1+1 +requiredVersion=8.2.1+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index f22b316e19a..cc571deab20 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -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 file identifies the root of the test-suite hierarchy. @@ -123,7 +123,7 @@ requires.properties= \ jdk.static # Minimum jtreg version -requiredVersion=8.1+1 +requiredVersion=8.2.1+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/langtools/TEST.ROOT b/test/langtools/TEST.ROOT index 14636716882..434cf91b0ec 100644 --- a/test/langtools/TEST.ROOT +++ b/test/langtools/TEST.ROOT @@ -15,7 +15,7 @@ keys=intermittent randomness needs-src needs-src-jdk_javadoc groups=TEST.groups # Minimum jtreg version -requiredVersion=8.1+1 +requiredVersion=8.2.1+1 # Use new module options useNewOptions=true diff --git a/test/lib-test/TEST.ROOT b/test/lib-test/TEST.ROOT index ebdf3f1a334..f23d38c1e66 100644 --- a/test/lib-test/TEST.ROOT +++ b/test/lib-test/TEST.ROOT @@ -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 @@ -29,7 +29,7 @@ keys=randomness # Minimum jtreg version -requiredVersion=8.1+1 +requiredVersion=8.2.1+1 # Allow querying of various System properties in @requires clauses requires.extraPropDefns = ../jtreg-ext/requires/VMProps.java From 57931dc6b24af2c02206b01bcc417e5607d39371 Mon Sep 17 00:00:00 2001 From: Leo Korinth Date: Wed, 11 Feb 2026 09:14:58 +0000 Subject: [PATCH 212/215] 8377172: Change datatype of CodeEntryAlignment to uint Reviewed-by: ayang, mhaessig --- src/hotspot/cpu/aarch64/globals_aarch64.hpp | 4 ++-- src/hotspot/cpu/arm/globals_arm.hpp | 4 ++-- src/hotspot/cpu/ppc/globals_ppc.hpp | 4 ++-- src/hotspot/cpu/riscv/globals_riscv.hpp | 4 ++-- src/hotspot/cpu/s390/globals_s390.hpp | 4 ++-- src/hotspot/cpu/x86/globals_x86.hpp | 6 ++--- src/hotspot/cpu/x86/macroAssembler_x86.cpp | 2 +- src/hotspot/cpu/zero/globals_zero.hpp | 4 ++-- src/hotspot/share/asm/codeBuffer.cpp | 6 ++--- src/hotspot/share/code/codeCache.cpp | 2 +- src/hotspot/share/interpreter/interpreter.hpp | 4 ++-- .../share/jvmci/jvmciCompilerToVMInit.cpp | 11 +++++----- src/hotspot/share/opto/constantTable.cpp | 4 ++-- .../flags/jvmFlagConstraintsCompiler.cpp | 22 +++++++++---------- .../flags/jvmFlagConstraintsCompiler.hpp | 4 ++-- src/hotspot/share/runtime/globals.hpp | 5 +++-- 16 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp index 8e520314c8b..a59e83c4b69 100644 --- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp @@ -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. * Copyright (c) 2015, 2019, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,7 +39,7 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap nulls define_pd_global(bool, DelayCompilerStubsGeneration, COMPILER2_OR_JVMCI); define_pd_global(size_t, CodeCacheSegmentSize, 64); -define_pd_global(intx, CodeEntryAlignment, 64); +define_pd_global(uint, CodeEntryAlignment, 64); define_pd_global(intx, OptoLoopAlignment, 16); #define DEFAULT_STACK_YELLOW_PAGES (2) diff --git a/src/hotspot/cpu/arm/globals_arm.hpp b/src/hotspot/cpu/arm/globals_arm.hpp index 363a9a2c25c..c568ea04122 100644 --- a/src/hotspot/cpu/arm/globals_arm.hpp +++ b/src/hotspot/cpu/arm/globals_arm.hpp @@ -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 @@ -37,7 +37,7 @@ define_pd_global(bool, TrapBasedNullChecks, false); // Not needed define_pd_global(bool, DelayCompilerStubsGeneration, false); // No need - only few compiler's stubs define_pd_global(size_t, CodeCacheSegmentSize, 64); -define_pd_global(intx, CodeEntryAlignment, 16); +define_pd_global(uint, CodeEntryAlignment, 16); define_pd_global(intx, OptoLoopAlignment, 16); #define DEFAULT_STACK_YELLOW_PAGES (2) diff --git a/src/hotspot/cpu/ppc/globals_ppc.hpp b/src/hotspot/cpu/ppc/globals_ppc.hpp index 41a8e821ada..927a8cc2be3 100644 --- a/src/hotspot/cpu/ppc/globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/globals_ppc.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. * Copyright (c) 2012, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -60,7 +60,7 @@ define_pd_global(bool, VMContinuations, true); // Use large code-entry alignment. define_pd_global(size_t, CodeCacheSegmentSize, 128); -define_pd_global(intx, CodeEntryAlignment, 64); +define_pd_global(uint, CodeEntryAlignment, 64); define_pd_global(intx, OptoLoopAlignment, 16); define_pd_global(intx, InlineSmallCode, 1500); diff --git a/src/hotspot/cpu/riscv/globals_riscv.hpp b/src/hotspot/cpu/riscv/globals_riscv.hpp index 390ed2daeb9..21b119266e2 100644 --- a/src/hotspot/cpu/riscv/globals_riscv.hpp +++ b/src/hotspot/cpu/riscv/globals_riscv.hpp @@ -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. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,7 +39,7 @@ define_pd_global(bool, UncommonNullCast, true); // Uncommon-trap nulls define_pd_global(bool, DelayCompilerStubsGeneration, COMPILER2_OR_JVMCI); define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment. -define_pd_global(intx, CodeEntryAlignment, 64); +define_pd_global(uint, CodeEntryAlignment, 64); define_pd_global(intx, OptoLoopAlignment, 16); #define DEFAULT_STACK_YELLOW_PAGES (2) diff --git a/src/hotspot/cpu/s390/globals_s390.hpp b/src/hotspot/cpu/s390/globals_s390.hpp index 07987ea3469..d110443adf8 100644 --- a/src/hotspot/cpu/s390/globals_s390.hpp +++ b/src/hotspot/cpu/s390/globals_s390.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. * Copyright (c) 2016, 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -43,7 +43,7 @@ define_pd_global(size_t, CodeCacheSegmentSize, 256); // Ideally, this is 256 (cache line size). This keeps code end data // on separate lines. But we reduced it to 64 since 256 increased // code size significantly by padding nops between IVC and second UEP. -define_pd_global(intx, CodeEntryAlignment, 64); +define_pd_global(uint, CodeEntryAlignment, 64); define_pd_global(intx, OptoLoopAlignment, 2); define_pd_global(intx, InlineSmallCode, 2000); diff --git a/src/hotspot/cpu/x86/globals_x86.hpp b/src/hotspot/cpu/x86/globals_x86.hpp index 103e22d0185..4f5b6d31e75 100644 --- a/src/hotspot/cpu/x86/globals_x86.hpp +++ b/src/hotspot/cpu/x86/globals_x86.hpp @@ -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 @@ -46,9 +46,9 @@ define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRES // the uep and the vep doesn't get real alignment but just slops on by // only assured that the entry instruction meets the 5 byte size requirement. #if COMPILER2_OR_JVMCI -define_pd_global(intx, CodeEntryAlignment, 32); +define_pd_global(uint, CodeEntryAlignment, 32); #else -define_pd_global(intx, CodeEntryAlignment, 16); +define_pd_global(uint, CodeEntryAlignment, 16); #endif // COMPILER2_OR_JVMCI define_pd_global(intx, OptoLoopAlignment, 16); define_pd_global(intx, InlineSmallCode, 1000); diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index d4d3ec85bcf..83169df3456 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -765,7 +765,7 @@ void MacroAssembler::align32() { void MacroAssembler::align(uint modulus) { // 8273459: Ensure alignment is possible with current segment alignment - assert(modulus <= (uintx)CodeEntryAlignment, "Alignment must be <= CodeEntryAlignment"); + assert(modulus <= CodeEntryAlignment, "Alignment must be <= CodeEntryAlignment"); align(modulus, offset()); } diff --git a/src/hotspot/cpu/zero/globals_zero.hpp b/src/hotspot/cpu/zero/globals_zero.hpp index 6b6c6ea983c..6dc7d81275c 100644 --- a/src/hotspot/cpu/zero/globals_zero.hpp +++ b/src/hotspot/cpu/zero/globals_zero.hpp @@ -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. * Copyright 2007, 2008, 2009, 2010, 2011 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,7 +39,7 @@ define_pd_global(bool, UncommonNullCast, true); define_pd_global(bool, DelayCompilerStubsGeneration, false); // Don't have compiler's stubs define_pd_global(size_t, CodeCacheSegmentSize, 64 COMPILER1_AND_COMPILER2_PRESENT(+64)); // Tiered compilation has large code-entry alignment. -define_pd_global(intx, CodeEntryAlignment, 32); +define_pd_global(uint, CodeEntryAlignment, 32); define_pd_global(intx, OptoLoopAlignment, 16); define_pd_global(intx, InlineSmallCode, 1000); diff --git a/src/hotspot/share/asm/codeBuffer.cpp b/src/hotspot/share/asm/codeBuffer.cpp index d94f52c18f6..ba525588f32 100644 --- a/src/hotspot/share/asm/codeBuffer.cpp +++ b/src/hotspot/share/asm/codeBuffer.cpp @@ -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 @@ -468,9 +468,7 @@ void CodeBuffer::compute_final_layout(CodeBuffer* dest) const { assert(!_finalize_stubs, "non-finalized stubs"); { - // not sure why this is here, but why not... - int alignSize = MAX2((intx) sizeof(jdouble), CodeEntryAlignment); - assert( (dest->_total_start - _insts.start()) % alignSize == 0, "copy must preserve alignment"); + assert( (dest->_total_start - _insts.start()) % CodeEntryAlignment == 0, "copy must preserve alignment"); } const CodeSection* prev_cs = nullptr; diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 481eb51bd5c..2a0256cc316 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -1139,7 +1139,7 @@ size_t CodeCache::freelists_length() { void icache_init(); void CodeCache::initialize() { - assert(CodeCacheSegmentSize >= (size_t)CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points"); + assert(CodeCacheSegmentSize >= CodeEntryAlignment, "CodeCacheSegmentSize must be large enough to align entry points"); #ifdef COMPILER2 assert(CodeCacheSegmentSize >= (size_t)OptoLoopAlignment, "CodeCacheSegmentSize must be large enough to align inner loops"); #endif diff --git a/src/hotspot/share/interpreter/interpreter.hpp b/src/hotspot/share/interpreter/interpreter.hpp index 576146b344e..f7d42fcb4da 100644 --- a/src/hotspot/share/interpreter/interpreter.hpp +++ b/src/hotspot/share/interpreter/interpreter.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 @@ -59,7 +59,7 @@ class InterpreterCodelet: public Stub { // General info/converters int size() const { return _size; } static int alignment() { return HeapWordSize; } - static int code_alignment() { return CodeEntryAlignment; } + static uint code_alignment() { return CodeEntryAlignment; } // Code info address code_begin() const { return align_up((address)this + sizeof(InterpreterCodelet), code_alignment()); } diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index 3e9d8c97b88..6214f6a2746 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -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 @@ -355,7 +355,7 @@ JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) { return vmIntrinsics; } -#define PREDEFINED_CONFIG_FLAGS(do_bool_flag, do_int_flag, do_size_t_flag, do_intx_flag, do_uintx_flag) \ +#define PREDEFINED_CONFIG_FLAGS(do_bool_flag, do_uint_flag, do_int_flag, do_size_t_flag, do_intx_flag, do_uintx_flag) \ do_int_flag(AllocateInstancePrefetchLines) \ do_int_flag(AllocatePrefetchDistance) \ do_intx_flag(AllocatePrefetchInstr) \ @@ -367,7 +367,7 @@ JVMCIObjectArray CompilerToVM::initialize_intrinsics(JVMCI_TRAPS) { do_bool_flag(CITime) \ do_bool_flag(CITimeEach) \ do_size_t_flag(CodeCacheSegmentSize) \ - do_intx_flag(CodeEntryAlignment) \ + do_uint_flag(CodeEntryAlignment) \ do_int_flag(ContendedPaddingWidth) \ do_bool_flag(DontCompileHugeMethods) \ do_bool_flag(EagerJVMCI) \ @@ -554,16 +554,17 @@ jobjectArray readConfiguration0(JNIEnv *env, JVMCI_TRAPS) { JVMCIENV->put_object_at(vmFlags, i++, vmFlagObj); \ } #define ADD_BOOL_FLAG(name) ADD_FLAG(bool, name, BOXED_BOOLEAN) +#define ADD_UINT_FLAG(name) ADD_FLAG(uint, name, BOXED_LONG) #define ADD_INT_FLAG(name) ADD_FLAG(int, name, BOXED_LONG) #define ADD_SIZE_T_FLAG(name) ADD_FLAG(size_t, name, BOXED_LONG) #define ADD_INTX_FLAG(name) ADD_FLAG(intx, name, BOXED_LONG) #define ADD_UINTX_FLAG(name) ADD_FLAG(uintx, name, BOXED_LONG) - len = 0 + PREDEFINED_CONFIG_FLAGS(COUNT_FLAG, COUNT_FLAG, COUNT_FLAG, COUNT_FLAG, COUNT_FLAG); + len = 0 + PREDEFINED_CONFIG_FLAGS(COUNT_FLAG, COUNT_FLAG, COUNT_FLAG, COUNT_FLAG, COUNT_FLAG, COUNT_FLAG); JVMCIObjectArray vmFlags = JVMCIENV->new_VMFlag_array(len, JVMCI_CHECK_NULL); int i = 0; JVMCIObject value; - PREDEFINED_CONFIG_FLAGS(ADD_BOOL_FLAG, ADD_INT_FLAG, ADD_SIZE_T_FLAG, ADD_INTX_FLAG, ADD_UINTX_FLAG) + PREDEFINED_CONFIG_FLAGS(ADD_BOOL_FLAG, ADD_UINT_FLAG, ADD_INT_FLAG, ADD_SIZE_T_FLAG, ADD_INTX_FLAG, ADD_UINTX_FLAG) JVMCIObjectArray vmIntrinsics = CompilerToVM::initialize_intrinsics(JVMCI_CHECK_NULL); diff --git a/src/hotspot/share/opto/constantTable.cpp b/src/hotspot/share/opto/constantTable.cpp index aba71e45e1c..ae4ac336d93 100644 --- a/src/hotspot/share/opto/constantTable.cpp +++ b/src/hotspot/share/opto/constantTable.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 @@ -158,7 +158,7 @@ void ConstantTable::calculate_offsets_and_size() { // Align size up to the next section start (which is insts; see // CodeBuffer::align_at_start). assert(_size == -1, "already set?"); - _size = align_up(offset, (int)CodeEntryAlignment); + _size = align_up(offset, CodeEntryAlignment); } bool ConstantTable::emit(C2_MacroAssembler* masm) const { diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp index 47a02184d29..444ce321759 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.cpp @@ -163,10 +163,10 @@ JVMFlag::Error CodeCacheSegmentSizeConstraintFunc(size_t value, bool verbose) { return JVMFlag::VIOLATES_CONSTRAINT; } - if (CodeCacheSegmentSize < (size_t)CodeEntryAlignment) { + if (CodeCacheSegmentSize < CodeEntryAlignment) { JVMFlag::printError(verbose, "CodeCacheSegmentSize (%zu) must be " - "larger than or equal to CodeEntryAlignment (%zd) " + "larger than or equal to CodeEntryAlignment (%u) " "to align entry points\n", CodeCacheSegmentSize, CodeEntryAlignment); return JVMFlag::VIOLATES_CONSTRAINT; @@ -194,25 +194,25 @@ JVMFlag::Error CodeCacheSegmentSizeConstraintFunc(size_t value, bool verbose) { return JVMFlag::SUCCESS; } -JVMFlag::Error CodeEntryAlignmentConstraintFunc(intx value, bool verbose) { +JVMFlag::Error CodeEntryAlignmentConstraintFunc(uint value, bool verbose) { if (!is_power_of_2(value)) { JVMFlag::printError(verbose, - "CodeEntryAlignment (%zd) must be " + "CodeEntryAlignment (%u) must be " "a power of two\n", CodeEntryAlignment); return JVMFlag::VIOLATES_CONSTRAINT; } if (CodeEntryAlignment < 16) { JVMFlag::printError(verbose, - "CodeEntryAlignment (%zd) must be " + "CodeEntryAlignment (%u) must be " "greater than or equal to %d\n", CodeEntryAlignment, 16); return JVMFlag::VIOLATES_CONSTRAINT; } - if ((size_t)CodeEntryAlignment > CodeCacheSegmentSize) { + if (CodeEntryAlignment > CodeCacheSegmentSize) { JVMFlag::printError(verbose, - "CodeEntryAlignment (%zd) must be " + "CodeEntryAlignment (%u) must be " "less than or equal to CodeCacheSegmentSize (%zu) " "to align entry points\n", CodeEntryAlignment, CodeCacheSegmentSize); @@ -241,10 +241,10 @@ JVMFlag::Error OptoLoopAlignmentConstraintFunc(intx value, bool verbose) { return JVMFlag::VIOLATES_CONSTRAINT; } - if (OptoLoopAlignment > CodeEntryAlignment) { + if (checked_cast(OptoLoopAlignment) > CodeEntryAlignment) { JVMFlag::printError(verbose, "OptoLoopAlignment (%zd) must be " - "less or equal to CodeEntryAlignment (%zd)\n", + "less or equal to CodeEntryAlignment (%u)\n", value, CodeEntryAlignment); return JVMFlag::VIOLATES_CONSTRAINT; } @@ -339,10 +339,10 @@ JVMFlag::Error InitArrayShortSizeConstraintFunc(intx value, bool verbose) { #ifdef COMPILER2 JVMFlag::Error InteriorEntryAlignmentConstraintFunc(intx value, bool verbose) { - if (InteriorEntryAlignment > CodeEntryAlignment) { + if (checked_cast(InteriorEntryAlignment) > CodeEntryAlignment) { JVMFlag::printError(verbose, "InteriorEntryAlignment (%zd) must be " - "less than or equal to CodeEntryAlignment (%zd)\n", + "less than or equal to CodeEntryAlignment (%u)\n", InteriorEntryAlignment, CodeEntryAlignment); return JVMFlag::VIOLATES_CONSTRAINT; } diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp index 4544ad706fd..cf785800cfc 100644 --- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.hpp +++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsCompiler.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 @@ -41,7 +41,7 @@ f(intx, CompileThresholdConstraintFunc) \ f(intx, OnStackReplacePercentageConstraintFunc) \ f(size_t, CodeCacheSegmentSizeConstraintFunc) \ - f(intx, CodeEntryAlignmentConstraintFunc) \ + f(uint, CodeEntryAlignmentConstraintFunc) \ f(intx, OptoLoopAlignmentConstraintFunc) \ f(uintx, ArraycopyDstPrefetchDistanceConstraintFunc) \ f(uintx, ArraycopySrcPrefetchDistanceConstraintFunc) \ diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 6a2bbc9c648..6d4b9908e1c 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -1508,8 +1508,9 @@ const int ObjectAlignmentInBytes = 8; range(1, 1024) \ constraint(CodeCacheSegmentSizeConstraintFunc, AfterErgo) \ \ - product_pd(intx, CodeEntryAlignment, EXPERIMENTAL, \ - "Code entry alignment for generated code (in bytes)") \ + product_pd(uint, CodeEntryAlignment, EXPERIMENTAL, \ + "Code entry alignment for generated code" \ + " (in bytes, power of two)") \ constraint(CodeEntryAlignmentConstraintFunc, AfterErgo) \ \ product_pd(intx, OptoLoopAlignment, \ From 9026f49dd238d16240687c4627e42c5dbee08773 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 11 Feb 2026 09:23:52 +0000 Subject: [PATCH 213/215] 8377446: Improve parameter naming in pointer_delta() Reviewed-by: ayang, chagedorn --- src/hotspot/share/adlc/adlArena.cpp | 6 ++--- .../share/utilities/globalDefinitions.hpp | 23 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/adlc/adlArena.cpp b/src/hotspot/share/adlc/adlArena.cpp index ebd1f74911d..e3ae60e91a9 100644 --- a/src/hotspot/share/adlc/adlArena.cpp +++ b/src/hotspot/share/adlc/adlArena.cpp @@ -136,9 +136,9 @@ void *AdlArena::Acalloc( size_t items, size_t x ) { } //------------------------------realloc---------------------------------------- -static size_t pointer_delta(const void *left, const void *right) { - assert(left >= right, "pointer delta underflow"); - return (uintptr_t)left - (uintptr_t)right; +static size_t pointer_delta(const void* high, const void* low) { + assert(high >= low, "pointer delta underflow"); + return (uintptr_t)high - (uintptr_t)low; } // Reallocate storage in AdlArena. diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 54602297759..ea4f6f99ae6 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -433,7 +433,8 @@ typedef unsigned char u_char; typedef u_char* address; typedef const u_char* const_address; -// Pointer subtraction. +// Pointer subtraction, calculating high - low. Asserts on underflow. +// // The idea here is to avoid ptrdiff_t, which is signed and so doesn't have // the range we might need to find differences from one end of the heap // to the other. @@ -444,28 +445,28 @@ typedef const u_char* const_address; // and then additions like // ... top() + size ... // are safe because we know that top() is at least size below end(). -inline size_t pointer_delta(const volatile void* left, - const volatile void* right, +inline size_t pointer_delta(const volatile void* high, + const volatile void* low, size_t element_size) { - assert(left >= right, "avoid underflow - left: " PTR_FORMAT " right: " PTR_FORMAT, p2i(left), p2i(right)); - return (((uintptr_t) left) - ((uintptr_t) right)) / element_size; + assert(high >= low, "avoid underflow - high address: " PTR_FORMAT " low address: " PTR_FORMAT, p2i(high), p2i(low)); + return (((uintptr_t) high) - ((uintptr_t) low)) / element_size; } // A version specialized for HeapWord*'s. -inline size_t pointer_delta(const HeapWord* left, const HeapWord* right) { - return pointer_delta(left, right, sizeof(HeapWord)); +inline size_t pointer_delta(const HeapWord* high, const HeapWord* low) { + return pointer_delta(high, low, sizeof(HeapWord)); } // A version specialized for MetaWord*'s. -inline size_t pointer_delta(const MetaWord* left, const MetaWord* right) { - return pointer_delta(left, right, sizeof(MetaWord)); +inline size_t pointer_delta(const MetaWord* high, const MetaWord* low) { + return pointer_delta(high, low, sizeof(MetaWord)); } // pointer_delta_as_int is called to do pointer subtraction for nearby pointers that // returns a non-negative int, usually used as a size of a code buffer range. // This scales to sizeof(T). template -inline int pointer_delta_as_int(const volatile T* left, const volatile T* right) { - size_t delta = pointer_delta(left, right, sizeof(T)); +inline int pointer_delta_as_int(const volatile T* high, const volatile T* low) { + size_t delta = pointer_delta(high, low, sizeof(T)); assert(delta <= size_t(INT_MAX), "pointer delta out of range: %zu", delta); return static_cast(delta); } From e34291d8e11164ab3b6d0f6a3e8819bc29b32124 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Wed, 11 Feb 2026 09:24:18 +0000 Subject: [PATCH 214/215] 8377442: More fixes to ThreadLocalAllocBuffer after JDK-8377179 Reviewed-by: ayang, kbarrett, iwalulya --- src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp | 9 ++++++--- src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp | 8 +++++--- src/hotspot/share/runtime/thread.cpp | 2 +- src/hotspot/share/runtime/thread.hpp | 3 ++- src/hotspot/share/runtime/thread.inline.hpp | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp index e86881d3523..d99544c0573 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp @@ -459,13 +459,16 @@ size_t ThreadLocalAllocBuffer::end_reserve() { } size_t ThreadLocalAllocBuffer::estimated_used_bytes() const { + // Data races due to unsynchronized access like the following reads to _start + // and _top are undefined behavior. Atomic would not provide any additional + // guarantees, so use AtomicAccess directly. HeapWord* start = AtomicAccess::load(&_start); HeapWord* top = AtomicAccess::load(&_top); - // There has been a race when retrieving _top and _start. Return 0. - if (_top < _start) { + // If there has been a race when retrieving _top and _start, return 0. + if (top < start) { return 0; } - size_t used_bytes = pointer_delta(_top, _start, 1); + size_t used_bytes = pointer_delta(top, start, 1); // Comparing diff with the maximum allowed size will ensure that we don't add // the used bytes from a semi-initialized TLAB ending up with implausible values. // In this case also just return 0. diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp index a50e7c9533c..61caac7ec51 100644 --- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp +++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp @@ -32,8 +32,10 @@ class ThreadLocalAllocStats; -// ThreadLocalAllocBuffer: a descriptor for thread-local storage used by -// the threads for allocation. It is thread-private at any time. +// ThreadLocalAllocBuffer is a descriptor for thread-local storage used by +// mutator threads for local/private allocation. As a TLAB is thread-private, +// there is no concurrent/parallel access to its memory or its members, +// other than by estimated_used_bytes(). // // Heap sampling is performed via the end and allocation_end // fields. @@ -123,7 +125,7 @@ public: // Due to races with concurrent allocations and/or resetting the TLAB the return // value may be inconsistent with any other metrics (e.g. total allocated // bytes), and may just incorrectly return 0. - // Intented fo external inspection only where accuracy is not 100% required. + // Intended for external inspection only where accuracy is not 100% required. size_t estimated_used_bytes() const; // Allocate size HeapWords. The memory is NOT initialized to zero. diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 355dc70ae39..f27d355f56e 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -485,7 +485,7 @@ void Thread::print_on(outputStream* st, bool print_extended_info) const { (double)_statistical_info.getElapsedTime() / 1000.0 ); if (is_Java_thread() && (PrintExtendedThreadInfo || print_extended_info)) { - size_t allocated_bytes = (size_t) const_cast(this)->cooked_allocated_bytes(); + size_t allocated_bytes = checked_cast(cooked_allocated_bytes()); st->print("allocated=%zu%s ", byte_size_in_proper_unit(allocated_bytes), proper_unit_for_byte_size(allocated_bytes) diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index dc09d4c68c2..0225fa808cb 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -405,13 +405,14 @@ class Thread: public ThreadShadow { // Thread-Local Allocation Buffer (TLAB) support ThreadLocalAllocBuffer& tlab() { return _tlab; } + const ThreadLocalAllocBuffer& tlab() const { return _tlab; } void initialize_tlab(); void retire_tlab(ThreadLocalAllocStats* stats = nullptr); void fill_tlab(HeapWord* start, size_t pre_reserved, size_t new_size); jlong allocated_bytes() { return _allocated_bytes; } void incr_allocated_bytes(jlong size) { _allocated_bytes += size; } - inline jlong cooked_allocated_bytes(); + inline jlong cooked_allocated_bytes() const; ThreadHeapSampler& heap_sampler() { return _heap_sampler; } diff --git a/src/hotspot/share/runtime/thread.inline.hpp b/src/hotspot/share/runtime/thread.inline.hpp index b194f5e2a7f..d2ac34c3e46 100644 --- a/src/hotspot/share/runtime/thread.inline.hpp +++ b/src/hotspot/share/runtime/thread.inline.hpp @@ -36,7 +36,7 @@ #include "runtime/os.hpp" #endif -inline jlong Thread::cooked_allocated_bytes() { +inline jlong Thread::cooked_allocated_bytes() const { jlong allocated_bytes = AtomicAccess::load_acquire(&_allocated_bytes); size_t used_bytes = 0; if (UseTLAB) { From 1bce8e47383cb1f89d7325ce6645f4bb195f91ba Mon Sep 17 00:00:00 2001 From: Afshin Zafari Date: Wed, 11 Feb 2026 09:30:55 +0000 Subject: [PATCH 215/215] 8366957: Amalloc may return null despite contrary AllocFailType Reviewed-by: jsjolen, dholmes, kbarrett --- src/hotspot/share/memory/arena.cpp | 5 ++++- test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp | 14 +++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index 2de3f837c00..75cd909b028 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -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. * Copyright (c) 2019, 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -330,6 +330,9 @@ void* Arena::grow(size_t x, AllocFailType alloc_failmode) { size_t len = MAX2(ARENA_ALIGN(x), (size_t) Chunk::size); if (MemTracker::check_exceeds_limit(x, _mem_tag)) { + if (alloc_failmode == AllocFailStrategy::EXIT_OOM) { + vm_exit_out_of_memory(x, OOM_MALLOC_ERROR, "MallocLimit in Arena::grow"); + } return nullptr; } diff --git a/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp b/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp index da357285148..48da390e06f 100644 --- a/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp +++ b/test/hotspot/gtest/nmt/test_nmt_malloclimit.cpp @@ -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,6 +23,7 @@ */ #include "memory/allocation.hpp" +#include "memory/arena.hpp" #include "nmt/mallocLimit.hpp" #include "nmt/memTracker.hpp" #include "nmt/nmtCommon.hpp" @@ -155,3 +156,14 @@ TEST_VM_FATAL_ERROR_MSG(NMT, MallocLimitDeathTestOnStrDup, ".*MallocLimit: reach char* p = os::strdup("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", mtTest); } } + +TEST_VM_FATAL_ERROR_MSG(NMT, MallocLimitDeathTestOnArenaGrow, ".*MallocLimit in Arena::grow.*") { + // We fake the correct assert if NMT is off to make the test pass (there is no way to execute a death test conditionally) + if (!MemTracker::enabled()) { + fatal("Fake message please ignore: MallocLimit in Arena::grow"); + } + // the real test + MallocLimitHandler::initialize("test:10m:oom"); + Arena ar(mtTest); + ar.Amalloc(10 * M, AllocFailStrategy::EXIT_OOM); +}